From 161ff5c79d0d3bfe9a5c79a2a1412cd06109c276 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Fri, 6 Jul 2018 14:51:37 -0700 Subject: [PATCH] feat(bazel): protractor_web_test_suite for release (#24787) PR Close #24787 --- BUILD.bazel | 3 + WORKSPACE | 13 +- integration/bazel/BUILD.bazel | 4 + integration/bazel/WORKSPACE | 15 +- integration/bazel/package.json | 12 +- integration/bazel/protractor.conf.js | 5 - integration/bazel/src/BUILD.bazel | 8 +- integration/bazel/test/e2e/BUILD.bazel | 29 +- integration/bazel/test/e2e/app.spec.ts | 2 +- integration/bazel/test/e2e/on-prepare.ts | 12 + integration/bazel/test/e2e/tsconfig.json | 5 + integration/bazel/yarn.lock | 127 ++----- packages/bazel/index.bzl | 5 +- packages/bazel/src/BUILD.bazel | 1 + packages/bazel/src/protractor/BUILD.bazel | 13 +- packages/bazel/src/protractor/conf.js.tmpl | 14 - .../bazel/src/protractor/protractor.conf.js | 154 ++++++++ .../bazel/src/protractor/protractor_runner.js | 23 -- .../src/protractor/protractor_web_test.bzl | 341 ++++++++++++++++++ .../protractor/protractor_web_test_suite.bzl | 107 ------ .../bazel/src/protractor/utils/BUILD.bazel | 16 + packages/bazel/src/protractor/utils/index.ts | 94 +++++ .../bazel/src/protractor/utils/tsconfig.json | 6 + packages/bazel/test/protractor-2/BUILD.bazel | 54 +++ packages/bazel/test/protractor-2/app.ts | 12 + packages/bazel/test/protractor-2/conf.js | 11 + packages/bazel/test/protractor-2/index.html | 9 + .../bazel/test/protractor-2/on-prepare.js | 22 ++ packages/bazel/test/protractor-2/test.spec.ts | 28 ++ .../bazel/test/protractor-2/tsconfig.json | 5 + packages/bazel/test/protractor/BUILD.bazel | 13 +- packages/bazel/test/protractor/conf.js | 29 -- packages/bazel/test/protractor/conf.ts | 30 ++ packages/bazel/test/protractor/tsconfig.json | 5 + tools/http-server/http_server.bzl | 8 +- 35 files changed, 924 insertions(+), 311 deletions(-) create mode 100644 integration/bazel/test/e2e/on-prepare.ts create mode 100644 integration/bazel/test/e2e/tsconfig.json delete mode 100644 packages/bazel/src/protractor/conf.js.tmpl create mode 100644 packages/bazel/src/protractor/protractor.conf.js delete mode 100644 packages/bazel/src/protractor/protractor_runner.js create mode 100644 packages/bazel/src/protractor/protractor_web_test.bzl delete mode 100644 packages/bazel/src/protractor/protractor_web_test_suite.bzl create mode 100644 packages/bazel/src/protractor/utils/BUILD.bazel create mode 100644 packages/bazel/src/protractor/utils/index.ts create mode 100644 packages/bazel/src/protractor/utils/tsconfig.json create mode 100644 packages/bazel/test/protractor-2/BUILD.bazel create mode 100644 packages/bazel/test/protractor-2/app.ts create mode 100644 packages/bazel/test/protractor-2/conf.js create mode 100644 packages/bazel/test/protractor-2/index.html create mode 100644 packages/bazel/test/protractor-2/on-prepare.js create mode 100644 packages/bazel/test/protractor-2/test.spec.ts create mode 100644 packages/bazel/test/protractor-2/tsconfig.json delete mode 100644 packages/bazel/test/protractor/conf.js create mode 100644 packages/bazel/test/protractor/conf.ts create mode 100644 packages/bazel/test/protractor/tsconfig.json diff --git a/BUILD.bazel b/BUILD.bazel index e1bbba720c..42cbb6a5a2 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -35,6 +35,9 @@ node_modules_filegroup( "@types", "@webcomponents/custom-elements", ], + patterns = [ + "node_modules/protractor/**", + ], ) filegroup( diff --git a/WORKSPACE b/WORKSPACE index ccd1b3d331..a6c5b1ecf2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -13,15 +13,18 @@ http_archive( http_archive( name = "bazel_gazelle", - sha256 = "d03625db67e9fb0905bbd206fa97e32ae9da894fe234a493e7517fd25faec914", - url = "https://github.com/bazelbuild/bazel-gazelle/releases/download/0.10.1/bazel-gazelle-0.10.1.tar.gz", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/0.12.0/bazel-gazelle-0.12.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/0.12.0/bazel-gazelle-0.12.0.tar.gz", + ], + sha256 = "ddedc7aaeb61f2654d7d7d4fd7940052ea992ccdb031b8f9797ed143ac7e8d43", ) http_archive( name = "io_bazel_rules_webtesting", - url = "https://github.com/bazelbuild/rules_webtesting/archive/v0.2.0.zip", - strip_prefix = "rules_webtesting-0.2.0", - sha256 = "cecc12f07e95740750a40d38e8b14b76fefa1551bef9332cb432d564d693723c", + url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip", + strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2", + sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce", ) http_archive( diff --git a/integration/bazel/BUILD.bazel b/integration/bazel/BUILD.bazel index 356533e8bb..3307866a21 100644 --- a/integration/bazel/BUILD.bazel +++ b/integration/bazel/BUILD.bazel @@ -1,5 +1,9 @@ package(default_visibility = ["//visibility:public"]) +exports_files([ + "protractor.conf.js", +]) + filegroup( name = "node_modules", srcs = glob( diff --git a/integration/bazel/WORKSPACE b/integration/bazel/WORKSPACE index 946cd14f12..3f872be725 100644 --- a/integration/bazel/WORKSPACE +++ b/integration/bazel/WORKSPACE @@ -11,11 +11,20 @@ http_archive( sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c", ) +http_archive( + name = "bazel_gazelle", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/0.12.0/bazel-gazelle-0.12.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/0.12.0/bazel-gazelle-0.12.0.tar.gz", + ], + sha256 = "ddedc7aaeb61f2654d7d7d4fd7940052ea992ccdb031b8f9797ed143ac7e8d43", +) + http_archive( name = "io_bazel_rules_webtesting", - url = "https://github.com/bazelbuild/rules_webtesting/archive/v0.2.0.zip", - strip_prefix = "rules_webtesting-0.2.0", - sha256 = "cecc12f07e95740750a40d38e8b14b76fefa1551bef9332cb432d564d693723c", + url = "https://github.com/bazelbuild/rules_webtesting/archive/8fd9ce0fd9254bde251da0bc373d6cd08e811434.zip", + strip_prefix = "rules_webtesting-8fd9ce0fd9254bde251da0bc373d6cd08e811434", + sha256 = "4baee95fcfadfbaf868707af8accfd1cb98c5d13f808908e0152468bfb47f0f7", ) http_archive( diff --git a/integration/bazel/package.json b/integration/bazel/package.json index 5c2bb294f2..3730e3fdb9 100644 --- a/integration/bazel/package.json +++ b/integration/bazel/package.json @@ -18,17 +18,13 @@ "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@types/jasmine": "2.8.7", "@types/source-map": "0.5.1", - "concurrently": "3.5.1", "http-server": "0.11.1", "protractor": "file:../../node_modules/protractor", + "tsutils": "file:../../node_modules/tsutils", "typescript": "file:../../node_modules/typescript" }, "scripts": { - "postinstall": "concurrently \"webdriver-manager update $CHROMEDRIVER_VERSION_ARG\" \"ngc -p angular.tsconfig.json\"", - "test": "bazel build //... --noshow_progress && yarn e2e", - "pree2e": "bazel build test/...", - "e2e": "yarn e2e-prodserver && yarn e2e-devserver", - "e2e-prodserver": "concurrently \"bazel run //src:prodserver\" \"while ! nc -z 127.0.0.1 5432; do sleep 1; done && protractor\" --kill-others --success first", - "e2e-devserver": "concurrently \"bazel run //src:devserver\" \"while ! nc -z 127.0.0.1 5432; do sleep 1; done && protractor\" --kill-others --success first" + "postinstall": "ngc -p angular.tsconfig.json", + "test": "bazel build ... --noshow_progress && bazel test ..." } -} \ No newline at end of file +} diff --git a/integration/bazel/protractor.conf.js b/integration/bazel/protractor.conf.js index 5ca2ef42d2..6d86d03a38 100644 --- a/integration/bazel/protractor.conf.js +++ b/integration/bazel/protractor.conf.js @@ -1,9 +1,4 @@ exports.config = { - specs: ['bazel-bin/test/e2e/*.spec.js'], - capabilities: {browserName: 'chrome', chromeOptions: {args: ['--no-sandbox']}}, - directConnect: true, - baseUrl: 'http://localhost:5432/', - framework: 'jasmine', getPageTimeout: 60 * 1000, allScriptsTimeout: 60 * 1000, }; diff --git a/integration/bazel/src/BUILD.bazel b/integration/bazel/src/BUILD.bazel index c7a0f0249b..11933be462 100644 --- a/integration/bazel/src/BUILD.bazel +++ b/integration/bazel/src/BUILD.bazel @@ -1,3 +1,5 @@ +package(default_visibility = ["//visibility:public"]) + load("@angular//:index.bzl", "ng_module") # Allow targets under sub-packages to reference the tsconfig.json file @@ -46,15 +48,11 @@ genrule( nodejs_binary( name = "prodserver", - args = [ - "./src", - "-p", - "5432", - ], data = [ "index.html", ":bundle", ":zone.js", ], entry_point = "http-server/bin/http-server", + templated_args = ["src"], ) diff --git a/integration/bazel/test/e2e/BUILD.bazel b/integration/bazel/test/e2e/BUILD.bazel index f023d58b55..c4b5c4000b 100644 --- a/integration/bazel/test/e2e/BUILD.bazel +++ b/integration/bazel/test/e2e/BUILD.bazel @@ -1,7 +1,34 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") +load("@angular//:index.bzl", "protractor_web_test_suite") ts_library( name = "e2e", testonly = 1, - srcs = glob(["*.ts"]), + srcs = ["app.spec.ts"], +) + +ts_library( + name = "ts_on_prepare", + testonly = 1, + srcs = ["on-prepare.ts"], + tsconfig = ":tsconfig.json", + deps = ["@angular//src/protractor/utils"], +) + +protractor_web_test_suite( + name = "devserver_test", + configuration = "//:protractor.conf.js", + data = ["@angular//src/protractor/utils"], + on_prepare = ":ts_on_prepare", + server = "//src:devserver", + deps = [":e2e"], +) + +protractor_web_test_suite( + name = "prodserver_test", + configuration = "//:protractor.conf.js", + data = ["@angular//src/protractor/utils"], + on_prepare = ":ts_on_prepare", + server = "//src:prodserver", + deps = [":e2e"], ) diff --git a/integration/bazel/test/e2e/app.spec.ts b/integration/bazel/test/e2e/app.spec.ts index 69e93ccd8d..276d099a51 100644 --- a/integration/bazel/test/e2e/app.spec.ts +++ b/integration/bazel/test/e2e/app.spec.ts @@ -1,4 +1,4 @@ -import {browser, by, element, ExpectedConditions} from 'protractor'; +import {browser, by, element} from 'protractor'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 120000; diff --git a/integration/bazel/test/e2e/on-prepare.ts b/integration/bazel/test/e2e/on-prepare.ts new file mode 100644 index 0000000000..83c96a4ab6 --- /dev/null +++ b/integration/bazel/test/e2e/on-prepare.ts @@ -0,0 +1,12 @@ +import { browser } from 'protractor'; +import {OnPrepareConfig, runServer} from '@angular/bazel/protractor-utils'; + +export = function(config: OnPrepareConfig) { + const portFlag = config.server.endsWith('prodserver') ? '-p' : '-port'; + return runServer(config.workspace, config.server, portFlag, []) + .then(serverSpec => { + const serverUrl = `http://localhost:${serverSpec.port}`; + console.log(`Server has been started, starting tests against ${serverUrl}`); + browser.baseUrl = serverUrl; + }); +} diff --git a/integration/bazel/test/e2e/tsconfig.json b/integration/bazel/test/e2e/tsconfig.json new file mode 100644 index 0000000000..8f3c656796 --- /dev/null +++ b/integration/bazel/test/e2e/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "lib": ["es2015"] + } +} diff --git a/integration/bazel/yarn.lock b/integration/bazel/yarn.lock index 2612d49100..474faef7fb 100644 --- a/integration/bazel/yarn.lock +++ b/integration/bazel/yarn.lock @@ -3,12 +3,12 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: tslib "^1.9.0" "@angular/bazel@file:../../dist/packages-dist/bazel": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: "@bazel/typescript" "^0.15.0" "@types/node" "6.0.84" @@ -17,12 +17,12 @@ shelljs "0.7.8" "@angular/common@file:../../dist/packages-dist/common": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: tslib "^1.9.0" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: chokidar "^1.4.2" minimist "^1.2.0" @@ -30,22 +30,22 @@ tsickle "^0.29.0" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: tslib "^1.9.0" "@angular/core@file:../../dist/packages-dist/core": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: tslib "^1.9.0" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: tslib "^1.9.0" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "6.0.0-rc.5" + version "6.1.0-beta.1" dependencies: tslib "^1.9.0" @@ -74,16 +74,16 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" "@types/node@*": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.1.4.tgz#606651d3f8a8bec08b8cb262161aab9209f4a29d" + version "10.3.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.4.tgz#c74e8aec19e555df44609b8057311052a2c84d9e" "@types/node@6.0.84": version "6.0.84" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.84.tgz#193ffe5a9f42864d425ffd9739d95b753c6a1eab" "@types/node@^6.0.46": - version "6.0.111" - resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.111.tgz#85f880a1bab78d395a5de9bcb5319e73a0c31400" + version "6.0.113" + resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.113.tgz#4b41f38ad03e4b41f9dc259b3b58aecb22c9aebc" "@types/q@^0.0.32": version "0.0.32" @@ -132,10 +132,6 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ansi-regex@^0.2.0, ansi-regex@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -144,10 +140,6 @@ ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" -ansi-styles@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" - ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -286,16 +278,6 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" -chalk@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" - dependencies: - ansi-styles "^1.1.0" - escape-string-regexp "^1.0.0" - has-ansi "^0.1.0" - strip-ansi "^0.3.0" - supports-color "^0.2.0" - chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -355,27 +337,10 @@ combined-stream@1.0.6, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concurrently@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.1.tgz#ee8b60018bbe86b02df13e5249453c6ececd2521" - dependencies: - chalk "0.5.1" - commander "2.6.0" - date-fns "^1.23.0" - lodash "^4.5.1" - rx "2.3.24" - spawn-command "^0.0.2-1" - supports-color "^3.2.3" - tree-kill "^1.1.0" - console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" @@ -394,10 +359,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -date-fns@^1.23.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" - debug@2, debug@^2.1.2, debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -457,7 +418,7 @@ ecstatic@^3.0.0: minimist "^1.1.0" url-join "^2.0.2" -escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2: +escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -645,22 +606,12 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" -has-ansi@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" - dependencies: - ansi-regex "^0.2.0" - has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" dependencies: ansi-regex "^2.0.0" -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -894,10 +845,6 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lodash@^4.5.1: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - long@~3: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" @@ -1303,8 +1250,8 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolve@^1.1.6: - version "1.7.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" dependencies: path-parse "^1.0.5" @@ -1314,10 +1261,6 @@ rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1: dependencies: glob "^7.0.5" -rx@2.3.24: - version "2.3.24" - resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7" - "rxjs@file:../../node_modules/rxjs": version "6.0.0" dependencies: @@ -1327,7 +1270,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -1413,18 +1356,15 @@ source-map@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - sshpk@^1.7.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" dashdash "^1.12.0" getpass "^0.1.1" + safer-buffer "^2.0.2" optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" @@ -1452,12 +1392,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" - dependencies: - ansi-regex "^0.2.1" - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -1474,20 +1408,10 @@ strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -supports-color@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - dependencies: - has-flag "^1.0.0" - tar@^4: version "4.4.4" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" @@ -1516,10 +1440,6 @@ tough-cookie@~2.3.3: dependencies: punycode "^1.4.1" -tree-kill@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" - tsickle@^0.29.0: version "0.29.0" resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.29.0.tgz#812806554bb46c1aa16eb0fe2a051da95ca8f5a4" @@ -1529,10 +1449,15 @@ tsickle@^0.29.0: source-map "^0.6.0" source-map-support "^0.5.0" -tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e" +"tsutils@file:../../node_modules/tsutils": + version "2.20.0" + dependencies: + tslib "^1.8.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -1544,7 +1469,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" "typescript@file:../../node_modules/typescript": - version "2.7.2" + version "2.8.3" ultron@1.0.x: version "1.0.2" diff --git a/packages/bazel/index.bzl b/packages/bazel/index.bzl index 247618070e..1cb77d4e52 100644 --- a/packages/bazel/index.bzl +++ b/packages/bazel/index.bzl @@ -9,8 +9,11 @@ Users should not load files under "/src" load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module") load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package") -load("//packages/bazel/src/protractor:protractor_web_test_suite.bzl", _protractor_web_test_suite = "protractor_web_test_suite") +load("//packages/bazel/src/protractor:protractor_web_test.bzl", + _protractor_web_test = "protractor_web_test", + _protractor_web_test_suite = "protractor_web_test_suite") ng_module = _ng_module ng_package = _ng_package +protractor_web_test = _protractor_web_test protractor_web_test_suite = _protractor_web_test_suite diff --git a/packages/bazel/src/BUILD.bazel b/packages/bazel/src/BUILD.bazel index fbe921dbec..0f01be9729 100644 --- a/packages/bazel/src/BUILD.bazel +++ b/packages/bazel/src/BUILD.bazel @@ -5,6 +5,7 @@ filegroup( srcs = glob(["*"]) + [ "//packages/bazel/src/ng_package:package_assets", "//packages/bazel/src/ngc-wrapped:package_assets", + "//packages/bazel/src/protractor:package_assets", ], visibility = ["//packages/bazel:__subpackages__"], ) diff --git a/packages/bazel/src/protractor/BUILD.bazel b/packages/bazel/src/protractor/BUILD.bazel index e3be3b819b..98d1e3ffbc 100644 --- a/packages/bazel/src/protractor/BUILD.bazel +++ b/packages/bazel/src/protractor/BUILD.bazel @@ -1,4 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "package_assets", + srcs = glob(["*"]) + [ + "//packages/bazel/src/protractor/utils:package_assets", + ], + visibility = ["//packages/bazel:__subpackages__"], +) + exports_files([ - "conf.js.tmpl", - "protractor_runner.js", + "protractor.conf.js", ]) diff --git a/packages/bazel/src/protractor/conf.js.tmpl b/packages/bazel/src/protractor/conf.js.tmpl deleted file mode 100644 index 76dc1574db..0000000000 --- a/packages/bazel/src/protractor/conf.js.tmpl +++ /dev/null @@ -1,14 +0,0 @@ -const baseConf = require("BASE_CONF_IMPORT_PATH"); - -exports.config = { - ...baseConf.config, - framework: "jasmine2", - seleniumAddress: process.env.WEB_TEST_HTTP_SERVER.trim() + "/wd/hub", - specs: [ - SPEC_FILE_IMPORT_PATHS - ].map(specPath => require.resolve(specPath)), - // TODO: Allow users to specifify other browsers. - capabilities: { - browserName: "chrome" - }, -}; diff --git a/packages/bazel/src/protractor/protractor.conf.js b/packages/bazel/src/protractor/protractor.conf.js new file mode 100644 index 0000000000..70ee78a475 --- /dev/null +++ b/packages/bazel/src/protractor/protractor.conf.js @@ -0,0 +1,154 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const path = require('path'); + +const DEBUG = false; + +const configPath = 'TMPL_config'; +const onPreparePath = 'TMPL_on_prepare'; +const workspace = 'TMPL_workspace'; +const server = 'TMPL_server'; + +if (DEBUG) + console.info(`Protractor test starting with: + cwd: ${process.cwd()} + configPath: ${configPath} + onPreparePath: ${onPreparePath} + workspace: ${workspace} + server: ${server}`); + +// Helper function to warn when a user specified value is being overwritten +function setConf(conf, name, value, msg) { + if (conf[name] && conf[name] !== value) { + console.warn( + `Your protractor configuration specifies an option which is overwritten by Bazel: '${name}' ${msg}`); + } + conf[name] = value; +} + +let conf = {}; + +// Import the user's base protractor configuration if specified +if (configPath) { + const baseConf = require(configPath); + if (!baseConf.config) { + throw new Error('Invalid base protractor configration. Expected config to be exported.'); + } + conf = baseConf.config; +} + +// Import the user's on prepare function if specified +if (onPreparePath) { + const onPrepare = require(onPreparePath); + if (typeof onPrepare === 'function') { + const original = conf.onPrepare; + conf.onPrepare = function() { + return Promise.resolve(original ? original() : null) + .then(() => Promise.resolve(onPrepare({workspace, server}))); + }; + } else { + throw new Error( + 'Invalid protractor on_prepare script. Expected a function as the default export.'); + } +} + +// Override the user's base protractor configuration as appropriate based on the +// ts_web_test_suite & rules_webtesting WEB_TEST_METADATA attributes +setConf(conf, 'framework', 'jasmine2', 'is set to jasmine2'); + +const specs = [TMPL_specs] + .map(s => require.resolve(s)) + .filter(s => s.endsWith('.spec.js') || s.endsWith('.test.js')); + +setConf(conf, 'specs', specs, 'are determined by the srcs and deps attribute'); + +// WEB_TEST_METADATA is configured in rules_webtesting based on value +// of the browsers attribute passed to ts_web_test_suite +// We setup the protractor configuration based on the values in this object +if (process.env['WEB_TEST_METADATA']) { + const webTestMetadata = require(process.env['WEB_TEST_METADATA']); + if (DEBUG) console.info(`WEB_TEST_METADATA: ${JSON.stringify(webTestMetadata, null, 2)}`); + if (webTestMetadata['environment'] === 'sauce') { + // If a sauce labs browser is chosen for the test such as + // "@io_bazel_rules_webtesting//browsers/sauce:chrome-win10" + // than the 'environment' will equal 'sauce'. + // We expect that a SAUCE_USERNAME and SAUCE_ACCESS_KEY is available + // from the environment for this test to run + + // TODO(gmagolan): implement sauce labs support for protractor + throw new Error('Saucelabs not yet support by protractor_web_test_suite.'); + + // if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) { + // console.error('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are + // set.'); + // process.exit(1); + // } + // setConf(conf, 'sauceUser', process.env.SAUCE_USERNAME, 'is determined by the SAUCE_USERNAME + // environment variable'); + // setConf(conf, 'sauceKey', process.env.SAUCE_ACCESS_KEY, 'is determined by the + // SAUCE_ACCESS_KEY environment variable'); + } else if (webTestMetadata['environment'] === 'local') { + // When a local chrome or firefox browser is chosen such as + // "@io_bazel_rules_webtesting//browsers:chromium-local" or + // "@io_bazel_rules_webtesting//browsers:firefox-local" + // then the 'environment' will equal 'local' and + // 'webTestFiles' will contain the path to the binary to use + const webTestNamedFiles = webTestMetadata['webTestFiles'][0]['namedFiles']; + const headless = !process.env['DISPLAY']; + if (webTestNamedFiles['CHROMIUM']) { + const chromeBin = path.join(process.cwd(), 'external', webTestNamedFiles['CHROMIUM']); + const args = []; + if (headless) { + args.push('--headless'); + args.push('--disable-gpu'); + } + setConf(conf, 'directConnect', true, 'is set to true for chrome'); + setConf( + conf, 'chromeDriver', + path.join(process.cwd(), 'external', webTestNamedFiles['CHROMEDRIVER']), + 'is determined by the browsers attribute'); + setConf( + conf, 'capabilities', { + browserName: 'chrome', + chromeOptions: { + binary: chromeBin, + args: args, + } + }, + 'is determined by the browsers attribute'); + } + if (webTestNamedFiles['FIREFOX']) { + // TODO(gmagolan): implement firefox support for protractor + throw new Error('Firefox not yet support by protractor_web_test_suite'); + + // const firefoxBin = path.join('external', webTestNamedFiles['FIREFOX']); + // const args = []; + // if (headless) { + // args.push("--headless") + // args.push("--marionette") + // } + // setConf(conf, 'seleniumAddress', process.env.WEB_TEST_HTTP_SERVER.trim() + "/wd/hub", 'is + // configured by Bazel for firefox browser') + // setConf(conf, 'capabilities', { + // browserName: "firefox", + // 'moz:firefoxOptions': { + // binary: firefoxBin, + // args: args, + // } + // }, 'is determined by the browsers attribute'); + } + } else { + console.warn(`Unknown WEB_TEST_METADATA environment '${webTestMetadata['environment']}'`); + } +} + +// Export the complete protractor configuration +if (DEBUG) console.info(`Protractor configuration: ${JSON.stringify(conf, null, 2)}`); + +exports.config = conf; diff --git a/packages/bazel/src/protractor/protractor_runner.js b/packages/bazel/src/protractor/protractor_runner.js deleted file mode 100644 index 095373953f..0000000000 --- a/packages/bazel/src/protractor/protractor_runner.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - * - * @fileoverview A wrapper around the protractor cli for bazel compatibility. - */ - -const launcher = require('protractor/built/launcher'); - -function main(args) { - if (!args.length) { - throw new Error('Config file argument missing'); - } - const config = require.resolve(args[0]); - launcher.init(config); -} - -if (require.main === module) { - process.exitCode = main(process.argv.slice(2)); -} diff --git a/packages/bazel/src/protractor/protractor_web_test.bzl b/packages/bazel/src/protractor/protractor_web_test.bzl new file mode 100644 index 0000000000..3b66c4ee32 --- /dev/null +++ b/packages/bazel/src/protractor/protractor_web_test.bzl @@ -0,0 +1,341 @@ +# Copyright Google Inc. All Rights Reserved. +# +# Use of this source code is governed by an MIT-style license that can be +# found in the LICENSE file at https://angular.io/license +"""Implementation of the protractor_web_test and protractor_web_test_suite rules. +""" + +load("@build_bazel_rules_nodejs//internal:node.bzl", + "sources_aspect", + "expand_path_into_runfiles", +) +load("@io_bazel_rules_webtesting//web:web.bzl", "web_test_suite") +load("@io_bazel_rules_webtesting//web/internal:constants.bzl", "DEFAULT_WRAPPED_TEST_TAGS") +load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") + +_CONF_TMPL = "//packages/bazel/src/protractor:protractor.conf.js" + +def _protractor_web_test_impl(ctx): + configuration = ctx.actions.declare_file( + "%s.conf.js" % ctx.label.name, + sibling=ctx.outputs.executable) + + files = depset(ctx.files.srcs) + for d in ctx.attr.deps: + if hasattr(d, "node_sources"): + files = depset(transitive = [files, d.node_sources]) + elif hasattr(d, "files"): + files = depset(transitive = [files, d.files]) + + specs = [ + expand_path_into_runfiles(ctx, f.short_path) + for f in files + ] + + configuration_sources = [] + if ctx.file.configuration: + configuration_sources = [ctx.file.configuration] + if hasattr(ctx.attr.configuration, "node_sources"): + configuration_sources = ctx.attr.configuration.node_sources.to_list() + + configuration_file = ctx.file.configuration + if hasattr(ctx.attr.configuration, "typescript"): + configuration_file = ctx.attr.configuration.typescript.es5_sources.to_list()[0] + + on_prepare_sources = [] + if ctx.file.on_prepare: + on_prepare_sources = [ctx.file.on_prepare] + if hasattr(ctx.attr.on_prepare, "node_sources"): + on_prepare_sources = ctx.attr.on_prepare.node_sources.to_list() + + on_prepare_file = ctx.file.on_prepare + if hasattr(ctx.attr.on_prepare, "typescript"): + on_prepare_file = ctx.attr.on_prepare.typescript.es5_sources.to_list()[0] + + protractor_executable_path = ctx.executable.protractor.short_path + if protractor_executable_path.startswith('..'): + protractor_executable_path = "external" + protractor_executable_path[2:] + + server_executable_path = '' + if ctx.executable.server: + server_executable_path = ctx.executable.server.short_path + if server_executable_path.startswith('..'): + server_executable_path = "external" + protractor_executable_path[2:] + + ctx.actions.expand_template( + output = configuration, + template = ctx.file._conf_tmpl, + substitutions = { + "TMPL_config": expand_path_into_runfiles(ctx, configuration_file.short_path) if configuration_file else "", + "TMPL_on_prepare": expand_path_into_runfiles(ctx, on_prepare_file.short_path) if on_prepare_file else "", + "TMPL_workspace": ctx.workspace_name, + "TMPL_server": server_executable_path, + "TMPL_specs": "\n".join([" '%s'," % e for e in specs]), + }) + + runfiles = [configuration] + configuration_sources + on_prepare_sources + + ctx.actions.write( + output = ctx.outputs.executable, + is_executable = True, + content = """#!/usr/bin/env bash +if [ -e "$RUNFILE_MANIFEST_FILE" ]; then + while read line; do + declare -a PARTS=($line) + if [ "${{PARTS[0]}}" == "angular/{TMPL_protractor}" ]; then + readonly PROTRACTOR=${{PARTS[1]}} + elif [ "${{PARTS[0]}}" == "angular/{TMPL_conf}" ]; then + readonly CONF=${{PARTS[1]}} + fi + done < $RUNFILE_MANIFEST_FILE +else + readonly PROTRACTOR={TMPL_protractor} + readonly CONF={TMPL_conf} +fi + +export HOME=$(mktemp -d) + +# Print the protractor version in the test log +PROTRACTOR_VERSION=$($PROTRACTOR --version) +echo "Protractor $PROTRACTOR_VERSION" + +# Run the protractor binary +$PROTRACTOR $CONF +""".format(TMPL_protractor = protractor_executable_path, + TMPL_conf = configuration.short_path)) + return [DefaultInfo( + files = depset([ctx.outputs.executable]), + runfiles = ctx.runfiles( + files = runfiles, + transitive_files = files, + # Propagate protractor_bin and its runfiles + collect_data = True, + collect_default = True, + ), + executable = ctx.outputs.executable, + )] + +_protractor_web_test = rule( + implementation = _protractor_web_test_impl, + test = True, + executable = True, + attrs = { + "configuration": attr.label( + doc = "Protractor configuration file", + allow_single_file = True, + cfg = "data", + aspects = [sources_aspect]), + "srcs": attr.label_list( + doc = "A list of JavaScript test files", + allow_files = [".js"]), + "on_prepare": attr.label( + doc = """A file with a node.js script to run once before all tests run. + If the script exports a function which returns a promise, protractor + will wait for the promise to resolve before beginning tests.""", + allow_single_file = True, + cfg = "data", + aspects = [sources_aspect]), + "deps": attr.label_list( + doc = "Other targets which produce JavaScript such as `ts_library`", + allow_files = True, + aspects = [sources_aspect]), + "data": attr.label_list( + doc = "Runtime dependencies", + cfg = "data"), + "server": attr.label( + doc = "Optional server executable target", + executable = True, + cfg = "data", + single_file = False, + allow_files = True), + "protractor": attr.label( + doc = "Protractor executable target (set by protractor_web_test macro)", + executable = True, + cfg = "data", + single_file = False, + allow_files = True), + "_conf_tmpl": attr.label( + default = Label(_CONF_TMPL), + allow_single_file = True, + ), + }, +) + +def protractor_web_test( + name, + configuration = None, + on_prepare = None, + srcs = [], + deps = [], + data = [], + server = None, + tags = [], + **kwargs): + """Runs a protractor test in a browser. + + Args: + name: The name of the test + configuration: Protractor configuration file. + on_prepare: A file with a node.js script to run once before all tests run. + If the script exports a function which returns a promise, protractor + will wait for the promise to resolve before beginning tests. + srcs: JavaScript source files + deps: Other targets which produce JavaScript such as `ts_library` + data: Runtime dependencies + server: Optional server executable target + tags: Standard Bazel tags, this macro adds one for ibazel + **kwargs: passed through to `_protractor_web_test` + """ + + protractor_bin_name = name + "_protractor_bin" + + nodejs_binary( + name = protractor_bin_name, + entry_point = "protractor/bin/protractor", + data = srcs + deps + data, + node_modules = "@//:node_modules", + testonly = 1, + visibility = ["//visibility:private"], + ) + + # Our binary dependency must be in data[] for collect_data to pick it up + # FIXME: maybe we can just ask :protractor_bin_name for its runfiles attr + web_test_data = data + [":" + protractor_bin_name] + if server: + web_test_data += [server] + + _protractor_web_test( + name = name, + configuration = configuration, + on_prepare=on_prepare, + srcs = srcs, + deps = deps, + data = web_test_data, + server = server, + protractor = protractor_bin_name, + tags = tags + [ + # Users don't need to know that this tag is required to run under ibazel + "ibazel_notify_changes", + ], + **kwargs) + +def protractor_web_test_suite( + name, + configuration = None, + on_prepare = None, + srcs = [], + deps = [], + data = [], + server = None, + browsers=["@io_bazel_rules_webtesting//browsers:chromium-local"], + args=None, + browser_overrides=None, + config=None, + flaky=None, + local=None, + shard_count=None, + size=None, + tags = [], + test_suite_tags=None, + timeout=None, + visibility=None, + web_test_data=[], + wrapped_test_tags=None, + **remaining_keyword_args): + """Defines a test_suite of web_test targets that wrap a protractor_web_test target. + + Args: + name: The base name of the test. + configuration: Protractor configuration file. + on_prepare: A file with a node.js script to run once before all tests run. + If the script exports a function which returns a promise, protractor + will wait for the promise to resolve before beginning tests. + srcs: JavaScript source files + deps: Other targets which produce JavaScript such as `ts_library` + data: Runtime dependencies + server: Optional server executable target + browsers: A sequence of labels specifying the browsers to use. + args: Args for web_test targets generated by this extension. + browser_overrides: Dictionary; optional; default is an empty dictionary. A + dictionary mapping from browser names to browser-specific web_test + attributes, such as shard_count, flakiness, timeout, etc. For example: + {'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1} + '//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}. + config: Label; optional; Configuration of web test features. + flaky: A boolean specifying that the test is flaky. If set, the test will + be retried up to 3 times (default: 0) + local: boolean; optional. + shard_count: The number of test shards to use per browser. (default: 1) + size: A string specifying the test size. (default: 'large') + tags: A list of test tag strings to apply to each generated web_test target. + This macro adds a couple for ibazel. + test_suite_tags: A list of tag strings for the generated test_suite. + timeout: A string specifying the test timeout (default: computed from size) + visibility: List of labels; optional. + web_test_data: Data dependencies for the web_test. + wrapped_test_tags: A list of test tag strings to use for the wrapped test + **remaining_keyword_args: Arguments for the wrapped test target. + """ + # Check explicitly for None so that users can set this to the empty list + if wrapped_test_tags == None: + wrapped_test_tags = DEFAULT_WRAPPED_TEST_TAGS + + size = size or "large" + + wrapped_test_name = name + "_wrapped_test" + protractor_bin_name = name + "_protractor_bin" + + # Users don't need to know that this tag is required to run under ibazel + tags = tags + ["ibazel_notify_changes"] + + nodejs_binary( + name = protractor_bin_name, + entry_point = "protractor/bin/protractor", + data = srcs + deps + data, + node_modules = "@//:node_modules", + testonly = 1, + visibility = ["//visibility:private"], + ) + + # Our binary dependency must be in data[] for collect_data to pick it up + # FIXME: maybe we can just ask the :protractor_bin_name for its runfiles attr + web_test_data = web_test_data + [":" + protractor_bin_name] + if server: + web_test_data += [server] + + _protractor_web_test( + name=wrapped_test_name, + configuration=configuration, + on_prepare=on_prepare, + srcs=srcs, + deps=deps, + data=web_test_data, + server=server, + protractor=protractor_bin_name, + args=args, + flaky=flaky, + local=local, + shard_count=shard_count, + size=size, + tags=wrapped_test_tags, + timeout=timeout, + visibility=["//visibility:private"], + **remaining_keyword_args) + + web_test_suite( + name=name, + launcher=":"+wrapped_test_name, + args=args, + browsers=browsers, + browser_overrides=browser_overrides, + config=config, + data=web_test_data, + flaky=flaky, + local=local, + shard_count=shard_count, + size=size, + tags=tags, + test=wrapped_test_name, + test_suite_tags=test_suite_tags, + timeout=timeout, + visibility=visibility) diff --git a/packages/bazel/src/protractor/protractor_web_test_suite.bzl b/packages/bazel/src/protractor/protractor_web_test_suite.bzl deleted file mode 100644 index 8b8de712c7..0000000000 --- a/packages/bazel/src/protractor/protractor_web_test_suite.bzl +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright Google Inc. All Rights Reserved. -# -# Use of this source code is governed by an MIT-style license that can be -# found in the LICENSE file at https://angular.io/license -"""Implementation of the protractor_web_test_suite rule. -""" - -load("@io_bazel_rules_webtesting//web:web.bzl", "web_test_suite") -load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") -load("@build_bazel_rules_nodejs//internal/common:sources_aspect.bzl", "sources_aspect") -load("@build_bazel_rules_nodejs//internal/common:module_mappings.bzl", "module_mappings_runtime_aspect") - -def _modify_tsconfig_impl(ctx): - spec_file_import_paths = [] - for dep in ctx.attr.deps: - # For each transitive ES5 dependency, grab the short path - for f in dep.node_sources.to_list(): - spec_file_import_paths.append("\"{workspace}/{path}\"".format(workspace=ctx.workspace_name, path=f.short_path)) - - ctx.actions.expand_template( - template = ctx.file._conf_templ, - output = ctx.outputs._modified_conf, - substitutions = { - "BASE_CONF_IMPORT_PATH": "{workspace}/{path}".format(workspace=ctx.workspace_name, path=ctx.file.base_conf.short_path), - "SPEC_FILE_IMPORT_PATHS": ',\n '.join(spec_file_import_paths), - }) - -_modify_conf = rule( - attrs = { - "base_conf": attr.label( - doc = """conf.js file used to configure protractor.""", - allow_single_file = True, - cfg = "data", - aspects = [ - sources_aspect, - module_mappings_runtime_aspect, - ], - ), - "deps": attr.label_list( - doc = """Spec and page files used for testing.""", - allow_files = True, - cfg = "data", - aspects = [ - sources_aspect, - module_mappings_runtime_aspect, - ], - ), - "_conf_templ": attr.label( - allow_files = True, - single_file = True, - default = Label("@angular//packages/bazel/src/protractor:conf.js.tmpl"), - ), - }, - outputs = { - "_modified_conf": "%{name}.conf.js", - }, - implementation = _modify_tsconfig_impl, -) - -def protractor_web_test_suite(name, conf, deps, data = [], **kwargs): - """Runs protractor using the passed conf.js file and tests listed within deps. - - Args: - name: The name of the web_test_suite rule the macro expands into. - conf: A conf.js file to be used as a base template. The following fields of the base - config are overridden: - framework - seleniumAddress - specs - capabilities - deps: A list of dependencies containing the test files to run within protractor. - data: Any runtime files which are needed to run the test suite. - **kwargs: Any other arguements are passed directory to the expanded web_test_suite rule. - - Returns: - This macro expands into a web_test_suite rule which runs the protractor tests. - """ - - _modify_conf_name = "%s_modify_conf" % name - _modify_conf( - name = _modify_conf_name, - base_conf = conf, - deps = deps, - testonly = True, - ) - - _modified_conf = "%s.conf.js" % _modify_conf_name - - _protractor_runner_name = name + "_protractor_runner" - nodejs_binary( - name = _protractor_runner_name, - entry_point = "angular/packages/bazel/src/protractor/protractor_runner.js", - data = data + deps + [_modified_conf, "@angular//packages/bazel/src/protractor:protractor_runner.js"], - templated_args = ["$(location :%s)" % _modified_conf], - tags = ["manual"], - testonly = True, - ) - - web_test_suite( - name = name, - # TODO: Allow users to specify more browsers. Currently conf.js is hardcoded for chrome. - browsers = ["@io_bazel_rules_webtesting//browsers:chromium-local"], - data = data + deps + [conf, _modified_conf], - test = ":%s_bin" % _protractor_runner_name, - testonly = True, - **kwargs - ) diff --git a/packages/bazel/src/protractor/utils/BUILD.bazel b/packages/bazel/src/protractor/utils/BUILD.bazel new file mode 100644 index 0000000000..437e9b327e --- /dev/null +++ b/packages/bazel/src/protractor/utils/BUILD.bazel @@ -0,0 +1,16 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "package_assets", + srcs = glob(["*"]), + visibility = ["//packages/bazel:__subpackages__"], +) + +load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") + +ts_library( + name = "utils", + srcs = ["index.ts"], + module_name = "@angular/bazel/protractor-utils", + tsconfig = ":tsconfig.json", +) diff --git a/packages/bazel/src/protractor/utils/index.ts b/packages/bazel/src/protractor/utils/index.ts new file mode 100644 index 0000000000..666119d516 --- /dev/null +++ b/packages/bazel/src/protractor/utils/index.ts @@ -0,0 +1,94 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as child_process from 'child_process'; +import * as net from 'net'; +import * as path from 'path'; + +export function isTcpPortFree(port: number): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server.on('error', (e) => { resolve(false); }); + server.on('close', () => { resolve(true); }); + server.listen(port, () => { server.close(); }); + }); +} + +export function isTcpPortBound(port: number): Promise { + return new Promise((resolve, reject) => { + const client = new net.Socket(); + client.once('connect', () => { resolve(true); }); + client.once('error', (e) => { resolve(false); }); + client.connect(port); + }); +} + +export async function findFreeTcpPort(): Promise { + const range = { + min: 32768, + max: 60000, + }; + for (let i = 0; i < 100; i++) { + let port = Math.floor(Math.random() * (range.max - range.min) + range.min); + if (await isTcpPortFree(port)) { + return port; + } + } + throw new Error('Unable to find a free port'); +} + +// Interface for config parameter of the protractor_web_test_suite onPrepare function +export interface OnPrepareConfig { + // The workspace name + workspace: string; + + // The server binary to run + server: string; +} + +export function waitForServer(port: number, timeout: number): Promise { + return isTcpPortBound(port).then(isBound => { + if (!isBound) { + if (timeout <= 0) { + throw new Error('Timeout waiting for server to start'); + } + const wait = Math.min(timeout, 500); + return new Promise((res, rej) => setTimeout(res, wait)) + .then(() => waitForServer(port, timeout - wait)); + } + return true; + }); +} + +// Return type from runServer function +export interface ServerSpec { + // Port number that the server is running on + port: number; +} + +export function runServer( + workspace: string, binary: string, portFlag: string, args: string[], + timeout = 5000): Promise { + return findFreeTcpPort().then(function(port) { + const runfiles_path = process.env.TEST_SRCDIR; + const cmd = path.join(runfiles_path, workspace, binary); + + args = args.concat([portFlag, port.toString()]); + + const child = child_process.spawn( + cmd, args, {cwd: path.join(runfiles_path, workspace), stdio: 'inherit'}); + + child.on('exit', function(code) { + if (code != 0) { + throw new Error(`non-zero exit code ${code} from server`); + } + }); + + return waitForServer(port, timeout).then(() => { return {port}; }); + }); +} diff --git a/packages/bazel/src/protractor/utils/tsconfig.json b/packages/bazel/src/protractor/utils/tsconfig.json new file mode 100644 index 0000000000..e9b6c07f85 --- /dev/null +++ b/packages/bazel/src/protractor/utils/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "lib": ["es2015"] + } +} diff --git a/packages/bazel/test/protractor-2/BUILD.bazel b/packages/bazel/test/protractor-2/BUILD.bazel new file mode 100644 index 0000000000..696763d4c1 --- /dev/null +++ b/packages/bazel/test/protractor-2/BUILD.bazel @@ -0,0 +1,54 @@ +load("//packages/bazel:index.bzl", "protractor_web_test_suite") +load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver", "ts_library") +load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "rollup_bundle") +load("//tools/http-server:http_server.bzl", "http_server") + +ts_library( + name = "app", + srcs = ["app.ts"], +) + +ts_devserver( + name = "devserver", + serving_path = "/bundle.min.js", + static_files = ["index.html"], + deps = [":app"], +) + +rollup_bundle( + name = "bundle", + entry_point = "packages/bazel/test/protractor-2/app", + deps = [":app"], +) + +http_server( + name = "prodserver", + data = [ + "index.html", + ":bundle", + ], +) + +ts_library( + name = "ts_spec", + testonly = True, + srcs = ["test.spec.ts"], +) + +protractor_web_test_suite( + name = "prodserver_test", + configuration = ":conf.js", + data = ["//packages/bazel/src/protractor/utils"], + on_prepare = ":on-prepare.js", + server = ":prodserver", + deps = [":ts_spec"], +) + +protractor_web_test_suite( + name = "devserver_test", + configuration = ":conf.js", + data = ["//packages/bazel/src/protractor/utils"], + on_prepare = ":on-prepare.js", + server = ":prodserver", + deps = [":ts_spec"], +) diff --git a/packages/bazel/test/protractor-2/app.ts b/packages/bazel/test/protractor-2/app.ts new file mode 100644 index 0000000000..2008d9798a --- /dev/null +++ b/packages/bazel/test/protractor-2/app.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const el: HTMLDivElement = document.createElement('div'); +el.innerText = 'Hello, Protractor'; +el.className = 'ts1'; +document.body.appendChild(el); diff --git a/packages/bazel/test/protractor-2/conf.js b/packages/bazel/test/protractor-2/conf.js new file mode 100644 index 0000000000..5d142953a9 --- /dev/null +++ b/packages/bazel/test/protractor-2/conf.js @@ -0,0 +1,11 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +exports.config = { + onPrepare: function() { global.userOnPrepareGotCalled = true; } +}; diff --git a/packages/bazel/test/protractor-2/index.html b/packages/bazel/test/protractor-2/index.html new file mode 100644 index 0000000000..256a75f74f --- /dev/null +++ b/packages/bazel/test/protractor-2/index.html @@ -0,0 +1,9 @@ + + + + protractor test + + + + + diff --git a/packages/bazel/test/protractor-2/on-prepare.js b/packages/bazel/test/protractor-2/on-prepare.js new file mode 100644 index 0000000000..1ce5b2d426 --- /dev/null +++ b/packages/bazel/test/protractor-2/on-prepare.js @@ -0,0 +1,22 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const protractorUtils = require('@angular/bazel/protractor-utils'); +const protractor = require('protractor'); + +module.exports = function(config) { + if (!global.userOnPrepareGotCalled) { + throw new Error('Expecting user configuration onPrepare to have been called'); + } + const portFlag = config.server.endsWith('prodserver') ? '-p' : '-port'; + return protractorUtils.runServer(config.workspace, config.server, portFlag, []) + .then(serverSpec => { + const serverUrl = `http://localhost:${serverSpec.port}`; + protractor.browser.baseUrl = serverUrl; + }); +}; diff --git a/packages/bazel/test/protractor-2/test.spec.ts b/packages/bazel/test/protractor-2/test.spec.ts new file mode 100644 index 0000000000..cf8b5e9564 --- /dev/null +++ b/packages/bazel/test/protractor-2/test.spec.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + * + * @fileoverview A small demo of how to run a protractor test. + */ + +import {ExpectedConditions, browser, by, element} from 'protractor'; + + +// This test uses Protractor without Angular, so disable Angular features +browser.waitForAngularEnabled(false); + +describe('app', () => { + beforeAll(() => { + browser.get(''); + browser.wait(ExpectedConditions.presenceOf(element(by.css('div.ts1'))), 100000); + }); + + it('should display: Hello, Protractor', (done) => { + const div = element(by.css('div.ts1')); + div.getText().then(t => expect(t).toEqual(`Hello, Protractor`)); + done(); + }); +}); diff --git a/packages/bazel/test/protractor-2/tsconfig.json b/packages/bazel/test/protractor-2/tsconfig.json new file mode 100644 index 0000000000..8f3c656796 --- /dev/null +++ b/packages/bazel/test/protractor-2/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "lib": ["es2015"] + } +} diff --git a/packages/bazel/test/protractor/BUILD.bazel b/packages/bazel/test/protractor/BUILD.bazel index fe9ef9e9ec..ffd48d6ccb 100644 --- a/packages/bazel/test/protractor/BUILD.bazel +++ b/packages/bazel/test/protractor/BUILD.bazel @@ -7,8 +7,17 @@ ts_library( srcs = ["test.spec.ts"], ) +ts_library( + name = "ts_conf", + testonly = True, + srcs = ["conf.ts"], + tsconfig = ":tsconfig.json", + deps = ["//packages/bazel/src/protractor/utils"], +) + protractor_web_test_suite( - name = "demo", - conf = "conf.js", + name = "test", + configuration = ":ts_conf", + data = ["//packages/bazel/src/protractor/utils"], deps = [":ts_spec"], ) diff --git a/packages/bazel/test/protractor/conf.js b/packages/bazel/test/protractor/conf.js deleted file mode 100644 index 2ac5a3ce80..0000000000 --- a/packages/bazel/test/protractor/conf.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * @fileoverview A demo of what a user's conf.js file might look like. - */ -const http = require('http'); - -const PORT = 3000; - -exports.config = { - baseUrl: `http://localhost:${PORT}`, - onPrepare() { - const app = new http.Server(); - - app.on('request', (req, res) => { - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write('Hello World'); - res.end('\n'); - }); - - return new Promise(resolve => { app.listen(PORT, () => { resolve(); }); }); - } -}; diff --git a/packages/bazel/test/protractor/conf.ts b/packages/bazel/test/protractor/conf.ts new file mode 100644 index 0000000000..2a3e21f4be --- /dev/null +++ b/packages/bazel/test/protractor/conf.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as protractorUtils from '@angular/bazel/protractor-utils'; +import {browser} from 'protractor'; + +const http = require('http'); + +exports.config = { + onPrepare() { + return protractorUtils.findFreeTcpPort().then(port => { + const app = new http.Server(); + + app.on('request', (req, res) => { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write('Hello World'); + res.end('\n'); + }); + + browser.baseUrl = `http://localhost:${port}`; + + return new Promise(resolve => { app.listen(port, () => { resolve(); }); }); + }); + } +}; diff --git a/packages/bazel/test/protractor/tsconfig.json b/packages/bazel/test/protractor/tsconfig.json new file mode 100644 index 0000000000..8f3c656796 --- /dev/null +++ b/packages/bazel/test/protractor/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "lib": ["es2015"] + } +} diff --git a/tools/http-server/http_server.bzl b/tools/http-server/http_server.bzl index e835fd5ffc..a8ef4422ef 100644 --- a/tools/http-server/http_server.bzl +++ b/tools/http-server/http_server.bzl @@ -4,15 +4,15 @@ See https://www.npmjs.com/package/http-server """ load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") -def http_server(args = [], **kwargs): +def http_server(templated_args = [], **kwargs): # By default, we pass an argument pointing the http server to the # package of the caller. # This assumes there is an index.html in the package directory. - if not args: - args = [native.package_name()] + if not templated_args: + templated_args = [native.package_name()] nodejs_binary( node_modules = "@http-server_runtime_deps//:node_modules", entry_point = "http-server/bin/http-server", - args = args, + templated_args = templated_args, **kwargs)