summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/ng-schematics
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/ng-schematics')
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/.eslintignore5
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/.gitignore3
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/.mocharc.cjs6
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/CHANGELOG.md19
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/README.md67
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/package-lock.json1098
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/package.json68
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/builders/builders.json10
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/index.ts138
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/schema.json22
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/types.ts24
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/collection.json10
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/.puppeteerrc.cjs.template4
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tests/app.e2e.ts.template59
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tsconfig.json.template15
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/helpers/babel.js4
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/support/jasmine.json9
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jest/e2e/jest.config.js11
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/.mocharc.js4
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/babel.js4
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/node/e2e/.gitignore.template3
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/index.ts127
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/schema.json49
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/files.ts162
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/json.ts38
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/packages.ts204
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/types.ts28
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/test/src/index.spec.ts212
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/tools/copySchemaFiles.js72
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/tools/sandbox.js104
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/tsconfig.json19
-rw-r--r--remote/test/puppeteer/packages/ng-schematics/tsconfig.spec.json10
32 files changed, 2608 insertions, 0 deletions
diff --git a/remote/test/puppeteer/packages/ng-schematics/.eslintignore b/remote/test/puppeteer/packages/ng-schematics/.eslintignore
new file mode 100644
index 0000000000..69132ca336
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/.eslintignore
@@ -0,0 +1,5 @@
+# Ignore File that will be copied to Angular
+/files/
+
+# Ignore sandbox enviroment
+./sandbox/ \ No newline at end of file
diff --git a/remote/test/puppeteer/packages/ng-schematics/.gitignore b/remote/test/puppeteer/packages/ng-schematics/.gitignore
new file mode 100644
index 0000000000..93d9982369
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/.gitignore
@@ -0,0 +1,3 @@
+
+# Sandbox
+sandbox/ \ No newline at end of file
diff --git a/remote/test/puppeteer/packages/ng-schematics/.mocharc.cjs b/remote/test/puppeteer/packages/ng-schematics/.mocharc.cjs
new file mode 100644
index 0000000000..be9bc29919
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/.mocharc.cjs
@@ -0,0 +1,6 @@
+module.exports = {
+ logLevel: 'debug',
+ spec: 'test/build/**/*.spec.js',
+ exit: !!process.env.CI,
+ reporter: process.env.CI ? 'spec' : 'dot',
+};
diff --git a/remote/test/puppeteer/packages/ng-schematics/CHANGELOG.md b/remote/test/puppeteer/packages/ng-schematics/CHANGELOG.md
new file mode 100644
index 0000000000..3cc843df1e
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/CHANGELOG.md
@@ -0,0 +1,19 @@
+# Changelog
+
+## [0.2.0](https://github.com/puppeteer/puppeteer/compare/ng-schematics-v0.1.0...ng-schematics-v0.2.0) (2023-05-02)
+
+
+### ⚠ BREAKING CHANGES
+
+* drop support for node14 ([#10019](https://github.com/puppeteer/puppeteer/issues/10019))
+
+### Features
+
+* drop support for node14 ([#10019](https://github.com/puppeteer/puppeteer/issues/10019)) ([7405d65](https://github.com/puppeteer/puppeteer/commit/7405d6585aa09b240fbab09aa360674d4442b3d9))
+
+## 0.1.0 (2022-11-23)
+
+
+### Features
+
+* **ng-schematics:** Release @puppeteer/ng-schematics ([#9244](https://github.com/puppeteer/puppeteer/issues/9244)) ([be33929](https://github.com/puppeteer/puppeteer/commit/be33929770e473992ad49029e6d038d36591e108))
diff --git a/remote/test/puppeteer/packages/ng-schematics/README.md b/remote/test/puppeteer/packages/ng-schematics/README.md
new file mode 100644
index 0000000000..3efc89f17c
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/README.md
@@ -0,0 +1,67 @@
+# Puppeteer Angular Schematic
+
+Adds Puppeteer-based e2e tests to your Angular project.
+
+## Usage
+
+Run the command below in an Angular CLI app directory and follow the prompts.
+_Note this will add the schematic as a dependency to your project._
+
+```bash
+ng add @puppeteer/ng-schematics
+```
+
+Or you can use the same command followed by the [options](#options) below.
+
+Currently, this schematic supports the following test frameworks:
+
+- **Jasmine** [https://jasmine.github.io/]
+- **Jest** [https://jestjs.io/]
+- **Mocha** [https://mochajs.org/]
+- **Node Test Runner** _(Experimental)_ [https://nodejs.org/api/test.html]
+
+With the schematics installed you can run E2E tests:
+
+```bash
+ng e2e
+```
+
+> Note: Command spawns it's own server on the same port `ng serve` does.
+
+## Options
+
+When adding schematics to your project you can to provide following options:
+
+| Option | Description | Value | Required |
+| -------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -------- |
+| `--isDefaultTester` | When true, replaces default `ng e2e` command. | `boolean` | `true` |
+| `--exportConfig` | When true, creates an empty [Puppeteer configuration](https://pptr.dev/guides/configuration) file. (`.puppeteerrc.cjs`) | `boolean` | `true` |
+| `--testingFramework` | The testing framework to install along side Puppeteer. | `"jasmine"`, `"jest"`, `"mocha"`, `"node"` | `true` |
+
+## Contributing
+
+Check out our [contributing guide](https://pptr.dev/contributing) to get an overview of what you need to develop in the Puppeteer repo.
+
+### Sandbox
+
+For easier development we provide a script to auto-generate the Angular project to test against. Simply run:
+
+```bash
+npm run sandbox -- --init
+```
+
+After that to run `@puppeteer/ng-schematics` against the Sandbox Angular project run:
+
+```bash
+npm run sandbox
+# or to auto-build and then run schematics
+npm run sandbox -- --build
+```
+
+### Unit Testing
+
+The schematics utilize `@angular-devkit/schematics/testing` for verifying correct file creation and `package.json` updates. To execute the test suit:
+
+```bash
+npm run test
+```
diff --git a/remote/test/puppeteer/packages/ng-schematics/package-lock.json b/remote/test/puppeteer/packages/ng-schematics/package-lock.json
new file mode 100644
index 0000000000..11dd119c2b
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/package-lock.json
@@ -0,0 +1,1098 @@
+{
+ "name": "angular",
+ "version": "0.2.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "angular",
+ "version": "0.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "^14.2.6",
+ "@angular-devkit/schematics": "^14.2.6",
+ "typescript": "~4.7.2"
+ },
+ "devDependencies": {
+ "@types/jasmine": "~4.0.0",
+ "@types/node": "^14.15.0",
+ "jasmine": "^4.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/core": {
+ "version": "14.2.6",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.6.tgz",
+ "integrity": "sha512-qtRSdRm/h7C3ya04PJTDgQXV6mM8Y4RakANX1GTSXetCf9AVSxg74NJX76DWUgiHT4JiPYnJgJU6Hr/L0H6JOQ==",
+ "dependencies": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.1.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.4"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.5.2"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/schematics": {
+ "version": "14.2.6",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.6.tgz",
+ "integrity": "sha512-mSFtc4M49mWrYsgJx/P6bA6SzXb8SeZqmppKRMoEQxiXI1bwFdGLNWzAmzEsGvS96h/nPIaOfcX5cKJSp++4FA==",
+ "dependencies": {
+ "@angular-devkit/core": "14.2.6",
+ "jsonc-parser": "3.1.0",
+ "magic-string": "0.26.2",
+ "ora": "5.4.1",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@types/jasmine": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz",
+ "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "14.18.32",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.32.tgz",
+ "integrity": "sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==",
+ "dev": true
+ },
+ "node_modules/ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jasmine": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.4.0.tgz",
+ "integrity": "sha512-xrbOyYkkCvgduNw7CKktDtNb+BwwBv/zvQeHpTkbxqQ37AJL5V4sY3jHoMIJPP/hTc3QxLVwOyxc87AqA+kw5g==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.6",
+ "jasmine-core": "^4.4.0"
+ },
+ "bin": {
+ "jasmine": "bin/jasmine.js"
+ }
+ },
+ "node_modules/jasmine-core": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.4.0.tgz",
+ "integrity": "sha512-+l482uImx5BVd6brJYlaHe2UwfKoZBqQfNp20ZmdNfsjGFTemGfqHLsXjKEW23w9R/m8WYeFc9JmIgjj6dUtAA==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz",
+ "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg=="
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.26.2",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz",
+ "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==",
+ "dependencies": {
+ "sourcemap-codec": "^1.4.8"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
+ "node_modules/source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
+ "node_modules/typescript": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ }
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "14.2.6",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.6.tgz",
+ "integrity": "sha512-qtRSdRm/h7C3ya04PJTDgQXV6mM8Y4RakANX1GTSXetCf9AVSxg74NJX76DWUgiHT4JiPYnJgJU6Hr/L0H6JOQ==",
+ "requires": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.1.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.4"
+ }
+ },
+ "@angular-devkit/schematics": {
+ "version": "14.2.6",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.6.tgz",
+ "integrity": "sha512-mSFtc4M49mWrYsgJx/P6bA6SzXb8SeZqmppKRMoEQxiXI1bwFdGLNWzAmzEsGvS96h/nPIaOfcX5cKJSp++4FA==",
+ "requires": {
+ "@angular-devkit/core": "14.2.6",
+ "jsonc-parser": "3.1.0",
+ "magic-string": "0.26.2",
+ "ora": "5.4.1",
+ "rxjs": "6.6.7"
+ }
+ },
+ "@types/jasmine": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz",
+ "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "14.18.32",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.32.tgz",
+ "integrity": "sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "requires": {
+ "ajv": "^8.0.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+ },
+ "bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "requires": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw=="
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "requires": {
+ "clone": "^1.0.2"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="
+ },
+ "is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
+ },
+ "jasmine": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.4.0.tgz",
+ "integrity": "sha512-xrbOyYkkCvgduNw7CKktDtNb+BwwBv/zvQeHpTkbxqQ37AJL5V4sY3jHoMIJPP/hTc3QxLVwOyxc87AqA+kw5g==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.6",
+ "jasmine-core": "^4.4.0"
+ }
+ },
+ "jasmine-core": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.4.0.tgz",
+ "integrity": "sha512-+l482uImx5BVd6brJYlaHe2UwfKoZBqQfNp20ZmdNfsjGFTemGfqHLsXjKEW23w9R/m8WYeFc9JmIgjj6dUtAA==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ },
+ "jsonc-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz",
+ "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg=="
+ },
+ "log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "requires": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ }
+ },
+ "magic-string": {
+ "version": "0.26.2",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz",
+ "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==",
+ "requires": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "requires": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
+ "source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
+ "typescript": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ=="
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "requires": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ }
+ }
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/package.json b/remote/test/puppeteer/packages/ng-schematics/package.json
new file mode 100644
index 0000000000..e7f645d4d3
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/package.json
@@ -0,0 +1,68 @@
+{
+ "name": "@puppeteer/ng-schematics",
+ "version": "0.2.0",
+ "description": "Puppeteer Angular schematics",
+ "scripts": {
+ "build": "wireit",
+ "clean": "tsc -b --clean && rm -rf lib && rm -rf test/build",
+ "dev:test": "npm run test --watch",
+ "dev": "npm run build --watch",
+ "test": "wireit",
+ "sandbox": "node tools/sandbox.js"
+ },
+ "wireit": {
+ "build": {
+ "command": "tsc -b && node tools/copySchemaFiles.js",
+ "clean": "if-file-deleted",
+ "files": [
+ "tsconfig.json",
+ "tsconfig.spec.json",
+ "src/**",
+ "test/src/**"
+ ],
+ "output": [
+ "lib/**",
+ "test/build/**"
+ ]
+ },
+ "test": {
+ "command": "mocha",
+ "dependencies": [
+ "build"
+ ]
+ }
+ },
+ "keywords": [
+ "angular",
+ "puppeteer",
+ "schematics"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/puppeteer/puppeteer/tree/main/packages/ng-schematics"
+ },
+ "author": "The Chromium Authors",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "dependencies": {
+ "@angular-devkit/architect": "^0.1502.7",
+ "@angular-devkit/core": "^15.2.7",
+ "@angular-devkit/schematics": "^15.2.7"
+ },
+ "devDependencies": {
+ "@types/node": "^14.15.0",
+ "@schematics/angular": "^14.2.8",
+ "@angular/cli": "^15.2.2"
+ },
+ "files": [
+ "lib",
+ "!*.tsbuildinfo"
+ ],
+ "ng-add": {
+ "save": "devDependencies"
+ },
+ "schematics": "./lib/schematics/collection.json",
+ "builders": "./lib/builders/builders.json"
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/builders/builders.json b/remote/test/puppeteer/packages/ng-schematics/src/builders/builders.json
new file mode 100644
index 0000000000..41079f7731
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/builders/builders.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "../../../../node_modules/@angular-devkit/architect/src/builders-schema.json",
+ "builders": {
+ "puppeteer": {
+ "implementation": "./puppeteer",
+ "schema": "./puppeteer/schema.json",
+ "description": "Run e2e test with Puppeteer"
+ }
+ }
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/index.ts b/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/index.ts
new file mode 100644
index 0000000000..45aec95152
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/index.ts
@@ -0,0 +1,138 @@
+import {spawn} from 'child_process';
+
+import {
+ createBuilder,
+ BuilderContext,
+ BuilderOutput,
+ targetFromTargetString,
+ BuilderRun,
+} from '@angular-devkit/architect';
+import {JsonObject} from '@angular-devkit/core';
+
+import {PuppeteerBuilderOptions} from './types.js';
+
+const terminalStyles = {
+ blue: '\u001b[34m',
+ green: '\u001b[32m',
+ bold: '\u001b[1m',
+ reverse: '\u001b[7m',
+ clear: '\u001b[0m',
+};
+
+function getError(executable: string, args: string[]) {
+ return (
+ `Puppeteer E2E tests failed!` +
+ '\n' +
+ `Error running '${executable}' with arguments '${args.join(' ')}'.` +
+ `\n` +
+ 'Please look at the output above to determine the issue!'
+ );
+}
+
+function getExecutable(command: string[]) {
+ const executable = command.shift()!;
+ const error = getError(executable, command);
+
+ if (executable === 'node') {
+ return {
+ executable: executable,
+ args: command,
+ error,
+ };
+ }
+
+ return {
+ executable: `./node_modules/.bin/${executable}`,
+ args: command,
+ error,
+ };
+}
+
+async function executeCommand(context: BuilderContext, command: string[]) {
+ await new Promise((resolve, reject) => {
+ context.logger.debug(`Trying to execute command - ${command.join(' ')}.`);
+ const {executable, args, error} = getExecutable(command);
+
+ const child = spawn(executable, args, {
+ cwd: context.workspaceRoot,
+ stdio: 'inherit',
+ });
+
+ child.on('error', message => {
+ console.log(message);
+ reject(error);
+ });
+
+ child.on('exit', code => {
+ if (code === 0) {
+ resolve(true);
+ } else {
+ reject(error);
+ }
+ });
+ });
+}
+
+function message(
+ message: string,
+ context: BuilderContext,
+ type: 'info' | 'success' = 'info'
+): void {
+ const color = type === 'info' ? terminalStyles.blue : terminalStyles.green;
+ context.logger.info(
+ `${terminalStyles.bold}${terminalStyles.reverse}${color}${message}${terminalStyles.clear}`
+ );
+}
+
+async function startServer(
+ options: PuppeteerBuilderOptions,
+ context: BuilderContext
+): Promise<BuilderRun> {
+ context.logger.debug('Trying to start server.');
+ const target = targetFromTargetString(options.devServerTarget);
+ const defaultServerOptions = await context.getTargetOptions(target);
+
+ const overrides = {
+ watch: false,
+ host: defaultServerOptions['host'],
+ port: defaultServerOptions['port'],
+ } as JsonObject;
+
+ message('Spawning test server...\n', context);
+ const server = await context.scheduleTarget(target, overrides);
+ const result = await server.result;
+ if (!result.success) {
+ throw new Error('Failed to spawn server! Stopping tests...');
+ }
+
+ return server;
+}
+
+async function executeE2ETest(
+ options: PuppeteerBuilderOptions,
+ context: BuilderContext
+): Promise<BuilderOutput> {
+ let server: BuilderRun | null = null;
+ try {
+ server = await startServer(options, context);
+
+ message('\nRunning tests...\n', context);
+ for (const command of options.commands) {
+ await executeCommand(context, command);
+ }
+
+ message('\nTest ran successfully!', context, 'success');
+ return {success: true};
+ } catch (error) {
+ if (error instanceof Error) {
+ return {success: false, error: error.message};
+ }
+ return {success: false, error: error as any};
+ } finally {
+ if (server) {
+ await server.stop();
+ }
+ }
+}
+
+export default createBuilder<PuppeteerBuilderOptions>(executeE2ETest) as any;
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/schema.json b/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/schema.json
new file mode 100644
index 0000000000..42e80f46d0
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/schema.json
@@ -0,0 +1,22 @@
+{
+ "title": "Puppeteer",
+ "description": "Options for Puppeteer Angular Schematics",
+ "type": "object",
+ "properties": {
+ "commands": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "item": {
+ "type": "string"
+ }
+ },
+ "description": "Commands to execute in the repo. Commands prefixed with `./node_modules/bin` (Exception: 'node')."
+ },
+ "devServerTarget": {
+ "type": "string",
+ "description": "Angular target that spawns the server."
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/types.ts b/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/types.ts
new file mode 100644
index 0000000000..b25d51b5f1
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/builders/puppeteer/types.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {JsonObject} from '@angular-devkit/core';
+
+type Command = [string, ...string[]];
+
+export interface PuppeteerBuilderOptions extends JsonObject {
+ commands: Command[];
+ devServerTarget: string;
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/collection.json b/remote/test/puppeteer/packages/ng-schematics/src/schematics/collection.json
new file mode 100644
index 0000000000..0cf38b799b
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/collection.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "../../../../node_modules/@angular-devkit/schematics/collection-schema.json",
+ "schematics": {
+ "ng-add": {
+ "description": "Add Puppeteer to an Angular project",
+ "factory": "./ng-add/index#ngAdd",
+ "schema": "./ng-add/schema.json"
+ }
+ }
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/.puppeteerrc.cjs.template b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/.puppeteerrc.cjs.template
new file mode 100644
index 0000000000..04f3f0d832
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/.puppeteerrc.cjs.template
@@ -0,0 +1,4 @@
+/**
+ * @type {import("puppeteer").Configuration}
+ */
+module.exports = {};
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tests/app.e2e.ts.template b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tests/app.e2e.ts.template
new file mode 100644
index 0000000000..2f98ef7d46
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tests/app.e2e.ts.template
@@ -0,0 +1,59 @@
+import * as puppeteer from 'puppeteer';
+<% if(testingFramework == 'node') { %>
+import {
+ describe,
+ it,
+ before,
+ beforeEach,
+ after,
+ afterEach,
+} from 'node:test';
+<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %>
+import * as assert from 'assert';
+<% } %>
+
+describe('App test', function () {
+ let browser: puppeteer.Browser;
+ let page: puppeteer.Page;
+
+<% if(testingFramework == 'jasmine' || testingFramework == 'jest') { %>
+ beforeAll(async () => {
+ browser = await puppeteer.launch();
+ });
+<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %>
+ before(async () => {
+ browser = await puppeteer.launch();
+ });
+<% } %>
+
+ beforeEach(async () => {
+ page = await browser.newPage();
+ await page.goto('<%= baseUrl %>');
+ });
+
+ afterEach(async () => {
+ await page.close();
+ });
+
+<% if(testingFramework == 'jasmine' || testingFramework == 'jest') { %>
+ afterAll(async () => {
+ await browser.close();
+ });
+<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %>
+ after(async () => {
+ await browser.close();
+ });
+<% } %>
+
+ it('is running', async function () {
+ const element = await page.waitForSelector(
+ 'text/<%= project %> app is running!'
+ );
+
+<% if(testingFramework == 'jasmine' || testingFramework == 'jest') { %>
+ expect(element).not.toBeNull();
+<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %>
+ assert.ok(element);
+<% } %>
+ });
+});
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tsconfig.json.template b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tsconfig.json.template
new file mode 100644
index 0000000000..438d04725f
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/base/e2e/tsconfig.json.template
@@ -0,0 +1,15 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+<% if(testingFramework == 'jest') { %>
+ "esModuleInterop": true,
+<% } %><% if(testingFramework == 'node') { %>
+ "module": "CommonJS",
+ "rootDir": "tests/",
+ "outDir": "test/",
+<% } %>
+ "types": ["<%= testingFramework %>"]
+ },
+ "include": ["tests/**/*.e2e.ts"]
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/helpers/babel.js b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/helpers/babel.js
new file mode 100644
index 0000000000..06259b39c8
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/helpers/babel.js
@@ -0,0 +1,4 @@
+require('@babel/register')({
+ extensions: ['.js', '.ts'],
+ presets: ['@babel/preset-env', '@babel/preset-typescript'],
+});
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/support/jasmine.json b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/support/jasmine.json
new file mode 100644
index 0000000000..7100e102f1
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jasmine/e2e/support/jasmine.json
@@ -0,0 +1,9 @@
+{
+ "spec_dir": "e2e",
+ "spec_files": ["**/*[eE]2[eE].ts"],
+ "helpers": ["helpers/babel.js", "helpers/**/*.{js|ts}"],
+ "env": {
+ "stopSpecOnExpectationFailure": false,
+ "random": true
+ }
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jest/e2e/jest.config.js b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jest/e2e/jest.config.js
new file mode 100644
index 0000000000..99cc594c97
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/jest/e2e/jest.config.js
@@ -0,0 +1,11 @@
+/*
+ * For a detailed explanation regarding each configuration property and type check, visit:
+ * https://jestjs.io/docs/configuration
+ */
+
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ testMatch: ['<rootDir>/tests/**/?(*.)+(e2e).[tj]s?(x)'],
+ preset: 'ts-jest',
+ testEnvironment: 'node',
+};
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/.mocharc.js b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/.mocharc.js
new file mode 100644
index 0000000000..63ca85e3eb
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/.mocharc.js
@@ -0,0 +1,4 @@
+module.exports = {
+ file: ['e2e/babel.js'],
+ spec: './e2e/tests/**/*.e2e.ts',
+};
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/babel.js b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/babel.js
new file mode 100644
index 0000000000..06259b39c8
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/mocha/e2e/babel.js
@@ -0,0 +1,4 @@
+require('@babel/register')({
+ extensions: ['.js', '.ts'],
+ presets: ['@babel/preset-env', '@babel/preset-typescript'],
+});
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/node/e2e/.gitignore.template b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/node/e2e/.gitignore.template
new file mode 100644
index 0000000000..35250fac0e
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/files/node/e2e/.gitignore.template
@@ -0,0 +1,3 @@
+# Compiled e2e tests output Node auto resolves files in folders named 'test'
+
+test/ \ No newline at end of file
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/index.ts b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/index.ts
new file mode 100644
index 0000000000..49c56bfc2f
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/index.ts
@@ -0,0 +1,127 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
+import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
+import {of} from 'rxjs';
+import {concatMap, map, scan} from 'rxjs/operators';
+
+import {
+ addBaseFiles,
+ addFrameworkFiles,
+ getNgCommandName,
+} from '../utils/files.js';
+import {getAngularConfig} from '../utils/json.js';
+import {
+ addPackageJsonDependencies,
+ addPackageJsonScripts,
+ getDependenciesFromOptions,
+ getPackageLatestNpmVersion,
+ DependencyType,
+ type NodePackage,
+ updateAngularJsonScripts,
+} from '../utils/packages.js';
+import {type SchematicsOptions} from '../utils/types.js';
+
+// You don't have to export the function as default. You can also have more than one rule
+// factory per file.
+export function ngAdd(options: SchematicsOptions): Rule {
+ return (tree: Tree, context: SchematicContext) => {
+ return chain([
+ addDependencies(options),
+ addPuppeteerFiles(options),
+ addOtherFiles(options),
+ updateScripts(options),
+ updateAngularConfig(options),
+ ])(tree, context);
+ };
+}
+
+function addDependencies(options: SchematicsOptions): Rule {
+ return (tree: Tree, context: SchematicContext) => {
+ context.logger.debug('Adding dependencies to "package.json"');
+ const dependencies = getDependenciesFromOptions(options);
+
+ return of(...dependencies).pipe(
+ concatMap((packageName: string) => {
+ return getPackageLatestNpmVersion(packageName);
+ }),
+ scan((array, nodePackage) => {
+ array.push(nodePackage);
+ return array;
+ }, [] as NodePackage[]),
+ map(packages => {
+ context.logger.debug('Updating dependencies...');
+ addPackageJsonDependencies(tree, packages, DependencyType.Dev);
+ context.addTask(new NodePackageInstallTask());
+
+ return tree;
+ })
+ );
+ };
+}
+
+function updateScripts(options: SchematicsOptions): Rule {
+ return (tree: Tree, context: SchematicContext): Tree => {
+ context.logger.debug('Updating "package.json" scripts');
+ const angularJson = getAngularConfig(tree);
+ const projects = Object.keys(angularJson['projects']);
+
+ if (projects.length === 1) {
+ const name = getNgCommandName(options);
+ const prefix = options.isDefaultTester ? '' : `run ${projects[0]}:`;
+ return addPackageJsonScripts(tree, [
+ {
+ name,
+ script: `ng ${prefix}${name}`,
+ },
+ ]);
+ }
+ return tree;
+ };
+}
+
+function addPuppeteerFiles(options: SchematicsOptions): Rule {
+ return (tree: Tree, context: SchematicContext) => {
+ context.logger.debug('Adding Puppeteer base files.');
+ const {projects} = getAngularConfig(tree);
+
+ return addBaseFiles(tree, context, {
+ projects,
+ options,
+ });
+ };
+}
+
+function addOtherFiles(options: SchematicsOptions): Rule {
+ return (tree: Tree, context: SchematicContext) => {
+ context.logger.debug('Adding Puppeteer additional files.');
+ const {projects} = getAngularConfig(tree);
+
+ return addFrameworkFiles(tree, context, {
+ projects,
+ options,
+ });
+ };
+}
+
+function updateAngularConfig(options: SchematicsOptions): Rule {
+ return (tree: Tree, context: SchematicContext): Tree => {
+ context.logger.debug('Updating "angular.json".');
+
+ return updateAngularJsonScripts(tree, options);
+ };
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/schema.json b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/schema.json
new file mode 100644
index 0000000000..77cb5bdd48
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/ng-add/schema.json
@@ -0,0 +1,49 @@
+{
+ "$schema": "http://json-schema.org/schema",
+ "$id": "Puppeteer",
+ "title": "Puppeteer Install Schema",
+ "type": "object",
+ "properties": {
+ "isDefaultTester": {
+ "description": "",
+ "type": "boolean",
+ "default": true,
+ "x-prompt": "Use Puppeteer as default `ng e2e` command?"
+ },
+ "exportConfig": {
+ "description": "",
+ "type": "boolean",
+ "default": false,
+ "x-prompt": "Export default Puppeteer config file?"
+ },
+ "testingFramework": {
+ "description": "",
+ "type": "string",
+ "enum": ["jasmine", "jest", "mocha", "node"],
+ "default": "jasmine",
+ "x-prompt": {
+ "message": "With what Testing Library do you wish to integrate?",
+ "type": "list",
+ "items": [
+ {
+ "value": "jasmine",
+ "label": "Use Jasmine [https://jasmine.github.io/]"
+ },
+ {
+ "value": "jest",
+ "label": "Use Jest [https://jestjs.io/]"
+ },
+ {
+ "value": "mocha",
+ "label": "Use Mocha [https://mochajs.org/]"
+ },
+ {
+ "value": "node",
+ "label": "Use Node Test Runner (Experimental: Node v18) [https://nodejs.org/api/test.html]"
+ }
+ ]
+ }
+ }
+ },
+ "required": []
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/files.ts b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/files.ts
new file mode 100644
index 0000000000..9518e1d996
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/files.ts
@@ -0,0 +1,162 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {relative, resolve} from 'path';
+
+import {getSystemPath, normalize, strings} from '@angular-devkit/core';
+import {
+ SchematicContext,
+ Tree,
+ apply,
+ applyTemplates,
+ chain,
+ filter,
+ mergeWith,
+ move,
+ url,
+} from '@angular-devkit/schematics';
+
+import {SchematicsOptions, TestingFramework} from './types.js';
+
+export interface FilesOptions {
+ projects: any;
+ options: SchematicsOptions;
+ applyPath: string;
+ relativeToWorkspacePath: string;
+ movePath?: string;
+ filterPredicate?: (path: string) => boolean;
+}
+
+const PUPPETEER_CONFIG_TEMPLATE = '.puppeteerrc.cjs.template';
+
+export function addFiles(
+ tree: Tree,
+ context: SchematicContext,
+ {
+ projects,
+ options,
+ applyPath,
+ movePath,
+ relativeToWorkspacePath,
+ filterPredicate,
+ }: FilesOptions
+): any {
+ return chain(
+ Object.keys(projects).map(name => {
+ const project = projects[name];
+ const projectPath = resolve(getSystemPath(normalize(project.root)));
+ const workspacePath = resolve(getSystemPath(normalize('')));
+
+ const relativeToWorkspace = relative(
+ `${projectPath}${relativeToWorkspacePath}`,
+ workspacePath
+ );
+
+ const baseUrl = getProjectBaseUrl(project);
+
+ return mergeWith(
+ apply(url(applyPath), [
+ filter(
+ filterPredicate ??
+ (() => {
+ return true;
+ })
+ ),
+ move(movePath ? `${project.root}${movePath}` : project.root),
+ applyTemplates({
+ ...options,
+ ...strings,
+ root: project.root ? `${project.root}/` : project.root,
+ baseUrl,
+ project: name,
+ relativeToWorkspace,
+ }),
+ ])
+ );
+ })
+ )(tree, context);
+}
+
+function getProjectBaseUrl(project: any): string {
+ let options = {protocol: 'http', port: 4200, host: 'localhost'};
+
+ if (project.architect?.serve?.options) {
+ const projectOptions = project.architect?.serve?.options;
+
+ options = {...options, ...projectOptions};
+ options.protocol = projectOptions.ssl ? 'https' : 'http';
+ }
+
+ return `${options.protocol}://${options.host}:${options.port}`;
+}
+
+export function addBaseFiles(
+ tree: Tree,
+ context: SchematicContext,
+ filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>
+): any {
+ const options: FilesOptions = {
+ ...filesOptions,
+ applyPath: './files/base',
+ relativeToWorkspacePath: `/`,
+ filterPredicate: path => {
+ return path.includes(PUPPETEER_CONFIG_TEMPLATE) &&
+ !filesOptions.options.exportConfig
+ ? false
+ : true;
+ },
+ };
+
+ return addFiles(tree, context, options);
+}
+
+export function addFrameworkFiles(
+ tree: Tree,
+ context: SchematicContext,
+ filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>
+): any {
+ const testingFramework = filesOptions.options.testingFramework;
+ const options: FilesOptions = {
+ ...filesOptions,
+ applyPath: `./files/${testingFramework}`,
+ relativeToWorkspacePath: `/`,
+ };
+
+ return addFiles(tree, context, options);
+}
+
+export function getScriptFromOptions(options: SchematicsOptions): string[][] {
+ switch (options.testingFramework) {
+ case TestingFramework.Jasmine:
+ return [[`jasmine`, '--config=./e2e/support/jasmine.json']];
+ case TestingFramework.Jest:
+ return [[`jest`, '-c', 'e2e/jest.config.js']];
+ case TestingFramework.Mocha:
+ return [[`mocha`, '--config=./e2e/.mocharc.js']];
+ case TestingFramework.Node:
+ return [
+ [`tsc`, '-p', 'e2e/tsconfig.json'],
+ ['node', '--test', 'e2e/'],
+ ];
+ }
+}
+
+export function getNgCommandName(options: SchematicsOptions): string {
+ if (options.isDefaultTester) {
+ return 'e2e';
+ }
+ return 'puppeteer';
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/json.ts b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/json.ts
new file mode 100644
index 0000000000..f48eab399e
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/json.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {SchematicsException, Tree} from '@angular-devkit/schematics';
+
+export function getJsonFileAsObject(
+ tree: Tree,
+ path: string
+): Record<string, any> {
+ try {
+ const buffer = tree.read(path) as Buffer;
+ const content = buffer.toString();
+ return JSON.parse(content);
+ } catch {
+ throw new SchematicsException(`Unable to retrieve file at ${path}.`);
+ }
+}
+
+export function getObjectAsJson(object: Record<string, any>): string {
+ return JSON.stringify(object, null, 2);
+}
+
+export function getAngularConfig(tree: Tree): Record<string, any> {
+ return getJsonFileAsObject(tree, './angular.json');
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/packages.ts b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/packages.ts
new file mode 100644
index 0000000000..b3724be90f
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/packages.ts
@@ -0,0 +1,204 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {get} from 'https';
+
+import {Tree} from '@angular-devkit/schematics';
+
+import {getNgCommandName, getScriptFromOptions} from './files.js';
+import {
+ getAngularConfig,
+ getJsonFileAsObject,
+ getObjectAsJson,
+} from './json.js';
+import {SchematicsOptions, TestingFramework} from './types.js';
+export interface NodePackage {
+ name: string;
+ version: string;
+}
+export interface NodeScripts {
+ name: string;
+ script: string;
+}
+
+export enum DependencyType {
+ Default = 'dependencies',
+ Dev = 'devDependencies',
+ Peer = 'peerDependencies',
+ Optional = 'optionalDependencies',
+}
+
+export function getPackageLatestNpmVersion(name: string): Promise<NodePackage> {
+ return new Promise(resolve => {
+ let version = 'latest';
+
+ return get(`https://registry.npmjs.org/${name}`, res => {
+ let data = '';
+
+ res.on('data', (chunk: any) => {
+ data += chunk;
+ });
+ res.on('end', () => {
+ try {
+ const response = JSON.parse(data);
+ version = response?.['dist-tags']?.latest ?? version;
+ } catch {
+ } finally {
+ resolve({
+ name,
+ version,
+ });
+ }
+ });
+ }).on('error', () => {
+ resolve({
+ name,
+ version,
+ });
+ });
+ });
+}
+
+function updateJsonValues(
+ json: Record<string, any>,
+ target: string,
+ updates: Array<{name: string; value: any}>,
+ overwrite = false
+) {
+ updates.forEach(({name, value}) => {
+ if (!json[target][name] || overwrite) {
+ json[target] = {
+ ...json[target],
+ [name]: value,
+ };
+ }
+ });
+}
+
+export function addPackageJsonDependencies(
+ tree: Tree,
+ packages: NodePackage[],
+ type: DependencyType,
+ overwrite?: boolean,
+ fileLocation = './package.json'
+): Tree {
+ const packageJson = getJsonFileAsObject(tree, fileLocation);
+
+ updateJsonValues(
+ packageJson,
+ type,
+ packages.map(({name, version}) => {
+ return {name, value: version};
+ }),
+ overwrite
+ );
+
+ tree.overwrite(fileLocation, getObjectAsJson(packageJson));
+
+ return tree;
+}
+
+export function getDependenciesFromOptions(
+ options: SchematicsOptions
+): string[] {
+ const dependencies = ['puppeteer'];
+ const babelPackages = [
+ '@babel/core',
+ '@babel/register',
+ '@babel/preset-env',
+ '@babel/preset-typescript',
+ ];
+
+ switch (options.testingFramework) {
+ case TestingFramework.Jasmine:
+ dependencies.push('jasmine', ...babelPackages);
+ break;
+ case TestingFramework.Jest:
+ dependencies.push('jest', '@types/jest', 'ts-jest');
+ break;
+ case TestingFramework.Mocha:
+ dependencies.push('mocha', '@types/mocha', ...babelPackages);
+ break;
+ case TestingFramework.Node:
+ dependencies.push('@types/node');
+ break;
+ }
+
+ return dependencies;
+}
+
+export function addPackageJsonScripts(
+ tree: Tree,
+ scripts: NodeScripts[],
+ overwrite?: boolean,
+ fileLocation = './package.json'
+): Tree {
+ const packageJson = getJsonFileAsObject(tree, fileLocation);
+
+ updateJsonValues(
+ packageJson,
+ 'scripts',
+ scripts.map(({name, script}) => {
+ return {name, value: script};
+ }),
+ overwrite
+ );
+
+ tree.overwrite(fileLocation, getObjectAsJson(packageJson));
+
+ return tree;
+}
+
+export function updateAngularJsonScripts(
+ tree: Tree,
+ options: SchematicsOptions,
+ overwrite = true
+): Tree {
+ const angularJson = getAngularConfig(tree);
+ const commands = getScriptFromOptions(options);
+ const name = getNgCommandName(options);
+
+ Object.keys(angularJson['projects']).forEach(project => {
+ const e2eScript = [
+ {
+ name,
+ value: {
+ builder: '@puppeteer/ng-schematics:puppeteer',
+ options: {
+ commands,
+ devServerTarget: `${project}:serve`,
+ },
+ configurations: {
+ production: {
+ devServerTarget: `${project}:serve:production`,
+ },
+ },
+ },
+ },
+ ];
+
+ updateJsonValues(
+ angularJson['projects'][project],
+ 'architect',
+ e2eScript,
+ overwrite
+ );
+ });
+
+ tree.overwrite('./angular.json', getObjectAsJson(angularJson));
+
+ return tree;
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/types.ts b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/types.ts
new file mode 100644
index 0000000000..c59f90e69d
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/src/schematics/utils/types.ts
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export enum TestingFramework {
+ Jasmine = 'jasmine',
+ Jest = 'jest',
+ Mocha = 'mocha',
+ Node = 'node',
+}
+
+export interface SchematicsOptions {
+ isDefaultTester: boolean;
+ exportConfig: boolean;
+ testingFramework: TestingFramework;
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/test/src/index.spec.ts b/remote/test/puppeteer/packages/ng-schematics/test/src/index.spec.ts
new file mode 100644
index 0000000000..59636dbb38
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/test/src/index.spec.ts
@@ -0,0 +1,212 @@
+import https from 'https';
+import {join} from 'path';
+
+import {JsonObject} from '@angular-devkit/core';
+import {
+ SchematicTestRunner,
+ UnitTestTree,
+} from '@angular-devkit/schematics/testing/schematic-test-runner';
+import expect from 'expect';
+import sinon from 'sinon';
+
+const WORKSPACE_OPTIONS = {
+ name: 'workspace',
+ newProjectRoot: 'projects',
+ version: '14.0.0',
+};
+
+const APPLICATION_OPTIONS = {
+ name: 'sandbox',
+};
+
+function getProjectFile(file: string): string {
+ return `/${WORKSPACE_OPTIONS.newProjectRoot}/${APPLICATION_OPTIONS.name}/${file}`;
+}
+
+function getAngularJsonScripts(
+ tree: UnitTestTree,
+ isDefault = true
+): {
+ builder: string;
+ configurations: Record<string, any>;
+ options: Record<string, any>;
+} {
+ const angularJson = tree.readJson('angular.json') as any;
+ const e2eScript = isDefault ? 'e2e' : 'puppeteer';
+ return angularJson['projects']?.[APPLICATION_OPTIONS.name]?.['architect'][
+ e2eScript
+ ];
+}
+
+function getPackageJson(tree: UnitTestTree): {
+ scripts: Record<string, string>;
+ devDependencies: string[];
+} {
+ const packageJson = tree.readJson('package.json') as JsonObject;
+ return {
+ scripts: packageJson['scripts'] as any,
+ devDependencies: Object.keys(
+ packageJson['devDependencies'] as Record<string, string>
+ ),
+ };
+}
+
+async function buildTestingTree(userOptions?: Record<string, any>) {
+ const runner = new SchematicTestRunner(
+ 'schematics',
+ join(__dirname, '../../lib/schematics/collection.json')
+ );
+ const options = {
+ isDefaultTester: true,
+ exportConfig: false,
+ testingFramework: 'jasmine',
+ ...userOptions,
+ };
+ let workingTree: UnitTestTree;
+
+ // Build workspace
+ workingTree = await runner
+ .runExternalSchematicAsync(
+ '@schematics/angular',
+ 'workspace',
+ WORKSPACE_OPTIONS
+ )
+ .toPromise();
+ // Build dummy application
+ workingTree = await runner
+ .runExternalSchematicAsync(
+ '@schematics/angular',
+ 'application',
+ APPLICATION_OPTIONS,
+ workingTree
+ )
+ .toPromise();
+
+ return await runner
+ .runSchematicAsync('ng-add', options, workingTree)
+ .toPromise();
+}
+
+describe('@puppeteer/ng-schematics: ng-add', () => {
+ // Stop outgoing Request for version fetching
+ before(() => {
+ const httpsGetStub = sinon.stub(https, 'get');
+ httpsGetStub.returns({
+ on: (_: any, callback: () => void) => {
+ callback();
+ },
+ } as any);
+ });
+
+ after(() => {
+ sinon.restore();
+ });
+
+ it('should create base files and update to "package.json"', async () => {
+ const tree = await buildTestingTree();
+ const {devDependencies, scripts} = getPackageJson(tree);
+ const {builder, configurations} = getAngularJsonScripts(tree);
+
+ expect(tree.files).toContain(getProjectFile('e2e/tsconfig.json'));
+ expect(tree.files).toContain(getProjectFile('e2e/tests/app.e2e.ts'));
+ expect(devDependencies).toContain('puppeteer');
+ expect(scripts['e2e']).toBe('ng e2e');
+ expect(builder).toBe('@puppeteer/ng-schematics:puppeteer');
+ expect(configurations).toEqual({
+ production: {
+ devServerTarget: 'sandbox:serve:production',
+ },
+ });
+ });
+
+ it('should update create proper "ng" command for non default tester', async () => {
+ const tree = await buildTestingTree({
+ isDefaultTester: false,
+ });
+ const {scripts} = getPackageJson(tree);
+ const {builder} = getAngularJsonScripts(tree, false);
+
+ expect(scripts['puppeteer']).toBe('ng run sandbox:puppeteer');
+ expect(builder).toBe('@puppeteer/ng-schematics:puppeteer');
+ });
+
+ it('should create Puppeteer config', async () => {
+ const {files} = await buildTestingTree({
+ exportConfig: true,
+ });
+
+ expect(files).toContain(getProjectFile('.puppeteerrc.cjs'));
+ });
+
+ it('should not create Puppeteer config', async () => {
+ const {files} = await buildTestingTree({
+ exportConfig: false,
+ });
+
+ expect(files).not.toContain(getProjectFile('.puppeteerrc.cjs'));
+ });
+
+ it('should create Jasmine files and update "package.json"', async () => {
+ const tree = await buildTestingTree({
+ testingFramework: 'jasmine',
+ });
+ const {devDependencies} = getPackageJson(tree);
+ const {options} = getAngularJsonScripts(tree);
+
+ expect(tree.files).toContain(getProjectFile('e2e/support/jasmine.json'));
+ expect(tree.files).toContain(getProjectFile('e2e/helpers/babel.js'));
+ expect(devDependencies).toContain('jasmine');
+ expect(devDependencies).toContain('@babel/core');
+ expect(devDependencies).toContain('@babel/register');
+ expect(devDependencies).toContain('@babel/preset-typescript');
+ expect(options['commands']).toEqual([
+ [`jasmine`, '--config=./e2e/support/jasmine.json'],
+ ]);
+ });
+
+ it('should create Jest files and update "package.json"', async () => {
+ const tree = await buildTestingTree({
+ testingFramework: 'jest',
+ });
+ const {devDependencies} = getPackageJson(tree);
+ const {options} = getAngularJsonScripts(tree);
+
+ expect(tree.files).toContain(getProjectFile('e2e/jest.config.js'));
+ expect(devDependencies).toContain('jest');
+ expect(devDependencies).toContain('@types/jest');
+ expect(devDependencies).toContain('ts-jest');
+ expect(options['commands']).toEqual([[`jest`, '-c', 'e2e/jest.config.js']]);
+ });
+
+ it('should create Mocha files and update "package.json"', async () => {
+ const tree = await buildTestingTree({
+ testingFramework: 'mocha',
+ });
+ const {devDependencies} = getPackageJson(tree);
+ const {options} = getAngularJsonScripts(tree);
+
+ expect(tree.files).toContain(getProjectFile('e2e/.mocharc.js'));
+ expect(tree.files).toContain(getProjectFile('e2e/babel.js'));
+ expect(devDependencies).toContain('mocha');
+ expect(devDependencies).toContain('@types/mocha');
+ expect(devDependencies).toContain('@babel/core');
+ expect(devDependencies).toContain('@babel/register');
+ expect(devDependencies).toContain('@babel/preset-typescript');
+ expect(options['commands']).toEqual([
+ [`mocha`, '--config=./e2e/.mocharc.js'],
+ ]);
+ });
+
+ it('should create Node files"', async () => {
+ const tree = await buildTestingTree({
+ testingFramework: 'node',
+ });
+ const {options} = getAngularJsonScripts(tree);
+
+ expect(tree.files).toContain(getProjectFile('e2e/.gitignore'));
+ expect(options['commands']).toEqual([
+ [`tsc`, '-p', 'e2e/tsconfig.json'],
+ ['node', '--test', 'e2e/'],
+ ]);
+ });
+});
diff --git a/remote/test/puppeteer/packages/ng-schematics/tools/copySchemaFiles.js b/remote/test/puppeteer/packages/ng-schematics/tools/copySchemaFiles.js
new file mode 100644
index 0000000000..bca61b8101
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/tools/copySchemaFiles.js
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const fs = require('fs/promises');
+const {join} = require('path');
+const path = require('path');
+
+/**
+ *
+ * @param {String} directory
+ * @param {String[]} files
+ */
+async function findSchemaFiles(directory, files = []) {
+ const items = await fs.readdir(directory);
+ const promises = [];
+ // Match any listing that has no *.* format
+ // Ignore files folder
+ const regEx = /^.*\.[^\s]*$/;
+
+ items.forEach(item => {
+ if (!item.match(regEx)) {
+ promises.push(findSchemaFiles(`${directory}/${item}`, files));
+ } else if (item.endsWith('.json') || directory.includes('files')) {
+ files.push(`${directory}/${item}`);
+ }
+ });
+
+ await Promise.all(promises);
+
+ return files;
+}
+
+async function copySchemaFiles() {
+ const srcDir = join(__dirname, '..', 'src');
+ const outputDir = join(__dirname, '..', 'lib');
+ const files = await findSchemaFiles(srcDir);
+
+ const moves = files.map(file => {
+ const to = file.replace(srcDir, outputDir);
+
+ return {from: file, to};
+ });
+
+ // Because fs.cp is Experimental (recursive support)
+ // We need to create directories first and copy the files
+ await Promise.all(
+ moves.map(({to}) => {
+ const dir = path.dirname(to);
+ return fs.mkdir(dir, {recursive: true});
+ })
+ );
+ await Promise.all(
+ moves.map(({from, to}) => {
+ return fs.copyFile(from, to);
+ })
+ );
+}
+
+copySchemaFiles();
diff --git a/remote/test/puppeteer/packages/ng-schematics/tools/sandbox.js b/remote/test/puppeteer/packages/ng-schematics/tools/sandbox.js
new file mode 100644
index 0000000000..44f131bfac
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/tools/sandbox.js
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2023 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const {spawn} = require('child_process');
+const {readFile, writeFile} = require('fs/promises');
+const {join} = require('path');
+const {cwd} = require('process');
+
+const isInit = process.argv.indexOf('--init') !== -1;
+const isBuild = process.argv.indexOf('--build') !== -1;
+const commands = {
+ build: ['npm run build'],
+ createSandbox: ['npx ng new sandbox --defaults'],
+ runSchematics: [
+ {
+ command: 'npm run schematics',
+ options: {
+ cwd: join(cwd(), '/sandbox/'),
+ },
+ },
+ ],
+};
+const scripts = {
+ // Deletes all files created by Puppeteer Ng-Schematics to avoid errors
+ 'delete:file':
+ 'rm -f .puppeteerrc.cjs && rm -f tsconfig.e2e.json && rm -R -f e2e/',
+ // Runs the Puppeteer Ng-Schematics against the sandbox
+ schematics: 'npm run delete:file && schematics ../:ng-add --dry-run=false',
+};
+/**
+ *
+ * @param {string | object} toExecute
+ * @returns {Promise<boolean>}
+ */
+async function executeCommand(commands) {
+ for (const toExecute of commands) {
+ let executable;
+ let args;
+ let options = {};
+ if (typeof toExecute === 'string') {
+ [executable, ...args] = toExecute.split(' ');
+ } else {
+ [executable, ...args] = toExecute.command.split(' ');
+ options = toExecute.options ?? {};
+ }
+
+ await new Promise((resolve, reject) => {
+ const createProcess = spawn(executable, args, {
+ stdio: 'inherit',
+ shell: true,
+ ...options,
+ });
+
+ createProcess.on('error', message => {
+ console.error(message);
+ reject(message);
+ });
+
+ createProcess.on('exit', code => {
+ if (code === 0) {
+ resolve(true);
+ } else {
+ reject();
+ }
+ });
+ });
+ }
+}
+
+async function main() {
+ if (isInit) {
+ await executeCommand(commands.createSandbox);
+
+ const packageJsonFile = join(cwd(), '/sandbox/package.json');
+ const packageJson = JSON.parse(await readFile(packageJsonFile));
+ packageJson['scripts'] = {
+ ...packageJson['scripts'],
+ ...scripts,
+ };
+ await writeFile(packageJsonFile, JSON.stringify(packageJson, null, 2));
+ } else {
+ if (isBuild) {
+ await executeCommand(commands.build);
+ }
+ await executeCommand(commands.runSchematics);
+ }
+}
+
+main().catch(() => {
+ console.log('\n');
+});
diff --git a/remote/test/puppeteer/packages/ng-schematics/tsconfig.json b/remote/test/puppeteer/packages/ng-schematics/tsconfig.json
new file mode 100644
index 0000000000..115c833159
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "baseUrl": "tsconfig",
+ "lib": ["ES2018"],
+ "module": "CommonJS",
+ "noEmitOnError": true,
+ "rootDir": "src/",
+ "outDir": "lib/",
+ "skipDefaultLibCheck": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "types": ["node"],
+ "target": "ES6"
+ },
+ "include": ["src/**/*"],
+ "exclude": ["src/**/files/**/*"],
+ "references": [{"path": "./tsconfig.spec.json"}]
+}
diff --git a/remote/test/puppeteer/packages/ng-schematics/tsconfig.spec.json b/remote/test/puppeteer/packages/ng-schematics/tsconfig.spec.json
new file mode 100644
index 0000000000..8afa532cb1
--- /dev/null
+++ b/remote/test/puppeteer/packages/ng-schematics/tsconfig.spec.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "test/src/",
+ "outDir": "test/build/",
+ "types": ["node", "mocha"]
+ },
+ "include": ["test/src/**/*"],
+ "exclude": ["test/build/**/*"]
+}