From 2b5e79c628af7a793f904c227df4c9adb2dd3552 Mon Sep 17 00:00:00 2001 From: Doug Roeper Date: Thu, 19 Dec 2019 07:34:43 -0500 Subject: [PATCH] - Adds an integration test - Incorporates changes from #69 --- .bazelrc | 4 + .clang-format | 115 +++++++++++ .github/workflows/ci.yaml | 24 ++- WORKSPACE | 93 ++++++++- package.json | 33 +++- postinstall.tsconfig.json | 35 ++++ src/BUILD.bazel | 5 + src/change_import_style.ts | 16 +- src/package.json | 17 ++ src/rules_typescript_proto_dependencies.bzl | 4 +- src/yarn.lock | 179 +++++++++++++++++ test/BUILD.bazel | 52 ++++- test/integration/BUILD.bazel | 43 ++++ test/integration/IntegrationTest.java | 183 ++++++++++++++++++ test/integration/client/BUILD.bazel | 180 +++++++++++++++++ test/integration/client/app.component.html | 9 + test/integration/client/app.component.ts | 32 +++ test/integration/client/app.module.ts | 16 ++ test/integration/client/index.dev.html | 13 ++ test/integration/client/index.prod.html | 19 ++ test/integration/client/index.scss | 63 ++++++ test/integration/client/main.dev.ts | 7 + test/integration/client/main.prod.ts | 9 + test/integration/client/require.config.js | 8 + test/integration/client/rollup.config.js | 17 ++ test/integration/client/rxjs_shims.js | 46 +++++ test/integration/envoy.yaml | 69 +++++++ test/integration/proto/BUILD.bazel | 35 ++++ test/integration/proto/service.proto | 15 ++ test/pizza_service_proto_test.spec.ts | 8 +- test/require.config.js | 3 +- test/rollup.config.js | 11 +- test/rollup_in_browser_test.spec.ts | 47 +++++ ...st.spec.js => rollup_in_node_test.spec.js} | 0 test/test_es6_bundling.d.ts | 4 + 35 files changed, 1384 insertions(+), 30 deletions(-) create mode 100644 .bazelrc create mode 100644 postinstall.tsconfig.json create mode 100644 src/package.json create mode 100644 src/yarn.lock create mode 100644 test/integration/BUILD.bazel create mode 100644 test/integration/IntegrationTest.java create mode 100644 test/integration/client/BUILD.bazel create mode 100644 test/integration/client/app.component.html create mode 100644 test/integration/client/app.component.ts create mode 100644 test/integration/client/app.module.ts create mode 100644 test/integration/client/index.dev.html create mode 100644 test/integration/client/index.prod.html create mode 100644 test/integration/client/index.scss create mode 100644 test/integration/client/main.dev.ts create mode 100644 test/integration/client/main.prod.ts create mode 100644 test/integration/client/require.config.js create mode 100644 test/integration/client/rollup.config.js create mode 100644 test/integration/client/rxjs_shims.js create mode 100644 test/integration/envoy.yaml create mode 100644 test/integration/proto/BUILD.bazel create mode 100644 test/integration/proto/service.proto create mode 100644 test/rollup_in_browser_test.spec.ts rename test/{rollup_test.spec.js => rollup_in_node_test.spec.js} (100%) create mode 100644 test/test_es6_bundling.d.ts diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..4547fe9 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,4 @@ +try-import user.bazelrc + +test --test_output=errors + diff --git a/.clang-format b/.clang-format index 290294f..520c2f3 100644 --- a/.clang-format +++ b/.clang-format @@ -228,3 +228,118 @@ SpacesInSquareBrackets: false Standard: Auto TabWidth: 8 UseTab: Never +--- +Language: Java +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +#RawStringFormats: +# - Delimiter: pb +# Language: TextProto +# BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 818f546..c9cae4d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,8 +10,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - name: Install bazel pre-reqs - run: sudo apt install pkg-config zip g++ zlib1g-dev unzip python3 + - name: Install deps + run: | + sudo apt install -y \ + pkg-config \ + zip \ + g++\ + zlib1g-dev \ + unzip \ + python3 \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg-agent \ + software-properties-common - name: Install bazelisk run: | wget https://github.com/philwo/bazelisk/archive/$BAZELISK_VERSION.tar.gz -O $BAZELISK_ARCHIVE @@ -22,6 +34,14 @@ jobs: env: BAZELISK_ARCHIVE: /tmp/bazelisk.tar.gz BAZELISK_VERSION: 8ec9f17a32c6d44abc2dbfb4a86b6b9d559253d0 + - name: Install envoy + run: | + curl -sL 'https://getenvoy.io/gpg' | sudo apt-key add - + sudo add-apt-repository \ + "deb [arch=amd64] https://dl.bintray.com/tetrate/getenvoy-deb $(lsb_release -cs) stable" + sudo apt-get update && sudo apt-get install -y getenvoy-envoy + which envoy + envoy --version - name: Build run: bazel build //...:all - name: Test diff --git a/WORKSPACE b/WORKSPACE index 74f4152..8ed2862 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,6 +4,35 @@ workspace( load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "build_bazel_rules_nodejs", + sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], +) + +http_archive( + name = "com_github_grpc_grpc", + sha256 = "950348037825ce013606f5b5b4291fc1d32f79e3c0437747af41813743aa5db8", + strip_prefix = "grpc-3990d9ce9e796abed897517994b3c93598b9525a", + urls = [ + "https://github.com/grpc/grpc/archive/3990d9ce9e796abed897517994b3c93598b9525a.tar.gz", + ], +) + +http_archive( + name = "io_grpc_grpc_java", + sha256 = "233cb87bdfde16602b446a655d204b56772f2395ebf0a5dfa8c4500893adc7f9", + strip_prefix = "grpc-java-57bc1910e4b23220e6a4b1f0f9326e5347e2ec41", + urls = ["https://github.com/grpc/grpc-java/archive/57bc1910e4b23220e6a4b1f0f9326e5347e2ec41.tar.gz"], +) + +http_archive( + name = "rules_jvm_external", + sha256 = "02e33287aa6fa129be0a3569ddba0c84ef8eb8b1e5f6f5348373bee559447642", + strip_prefix = "rules_jvm_external-05ba43aa5b671269cf0dfe2f91ec8f26dcea998e", + url = "https://github.com/bazelbuild/rules_jvm_external/archive/05ba43aa5b671269cf0dfe2f91ec8f26dcea998e.tar.gz", +) + http_archive( name = "rules_proto", sha256 = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0", @@ -14,11 +43,51 @@ http_archive( ) http_archive( - name = "build_bazel_rules_nodejs", - sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"], + name = "io_bazel_rules_sass", + sha256 = "e1af475dacad99c675042fcb3bf15dfaa197a3759821f0244f1b210d4f04d468", + strip_prefix = "rules_sass-1.24.0", + url = "https://github.com/bazelbuild/rules_sass/archive/1.24.0.tar.gz", +) + +http_archive( + name = "io_bazel_rules_webtesting", + sha256 = "9bb461d5ef08e850025480bab185fd269242d4e533bca75bfb748001ceb343c3", + urls = [ + "https://github.com/bazelbuild/rules_webtesting/releases/download/0.3.3/rules_webtesting.tar.gz", + ], +) + +################################################################################################### + +load("@rules_jvm_external//:defs.bzl", "maven_install") +load("@io_bazel_rules_webtesting//web:java_repositories.bzl", "RULES_WEBTESTING_ARTIFACTS") +load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS") + +maven_install( + artifacts = [ + "com.google.code.gson:gson:2.8.5", + "com.google.guava:guava:28.2-jre", + "com.google.truth:truth:0.42", + "com.google.truth.extensions:truth-java8-extension:0.42", + "io.netty:netty-tcnative-boringssl-static:2.0.25.Final", + "junit:junit:4.13-beta-1", + "net.bytebuddy:byte-buddy:1.8.22", + "org.seleniumhq.selenium:selenium-api:3.141.59", + "org.seleniumhq.selenium:selenium-remote-driver:3.141.59", + "org.seleniumhq.selenium:selenium-support:3.141.59", + ] + RULES_WEBTESTING_ARTIFACTS + IO_GRPC_GRPC_JAVA_ARTIFACTS, + generate_compat_repositories = True, + override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, + repositories = [ + "https://jcenter.bintray.com/", + "https://maven.google.com", + "https://repo1.maven.org/maven2", ) +load("@maven//:compat.bzl", "compat_repositories") + +compat_repositories() + load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() @@ -29,6 +98,14 @@ load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() +load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") + +grpc_deps() + +load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") + +grpc_java_repositories() + load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install") yarn_install( @@ -37,6 +114,12 @@ yarn_install( yarn_lock = "//:yarn.lock", ) +yarn_install( + name = "test_npm", + package_json = "//test:package.json", + yarn_lock = "//test:yarn.lock", +) + load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") install_bazel_dependencies() @@ -60,3 +143,7 @@ ts_setup_workspace() load("@rules_typescript_proto//:index.bzl", "rules_typescript_proto_dependencies") rules_typescript_proto_dependencies() + +load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") + +sass_repositories() diff --git a/package.json b/package.json index 1ec7dbb..9fb6ac7 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,46 @@ "repository": "https://github.com/Dig-Doug/rules_typescript_proto", "license": "Apache-2.0", "dependencies": { + "@angular/animations": "8.2.3", + "@angular/common": "8.2.3", + "@angular/core": "8.2.3", + "@angular/platform-browser": "8.2.3", + "@angular/platform-browser-dynamic": "8.2.3", "google-protobuf": "3.12.2" + "rxjs": "6.5.3", + "systemjs": "6.1.2", + "zone.js": "0.10.2" }, "devDependencies": { + "@angular/bazel": "9.0.0", + "@angular/cli": "8.3.10", + "@angular/compiler": "8.2.3", + "@angular/compiler-cli": "8.2.3", "@bazel/jasmine": "1.7.0", "@bazel/karma": "1.7.0", "@bazel/rollup": "1.7.0", "@bazel/typescript": "1.7.0", + "@babel/cli": "7.6.0", + "@babel/core": "7.6.0", + "@babel/preset-env": "7.6.0", + "@bazel/hide-bazel-files": "1.4.1", + "@bazel/jasmine": "1.4.1", + "@bazel/karma": "1.4.1", + "@bazel/rollup": "1.4.1", + "@bazel/terser": "1.4.1", + "@bazel/typescript": "1.4.1", "@improbable-eng/grpc-web": "0.12.0", + "@rollup/plugin-commonjs": "11.0.0", + "@rollup/plugin-node-resolve": "6.0.0", "@types/google-protobuf": "3.7.2", "@types/jasmine": "3.5.9", + "babelify": "^10.0.0", "clang-format": "1.4.0", + "core-js": "2.6.9", + "history-server": "1.3.1", "husky": "4.2.5", "karma": "5.0.1", + "html-insert-assets": "0.4.0", "karma-chrome-launcher": "3.1.0", "karma-firefox-launcher": "1.3.0", "karma-jasmine": "3.3.1", @@ -25,13 +52,13 @@ "minimist": "^1.2.5", "requirejs": "2.3.6", "rollup": "2.18.1", - "rollup-plugin-commonjs": "10.1.0", - "rollup-plugin-node-resolve": "5.2.0", + "terser": "4.3.9", "ts-protoc-gen": "0.12.0", "typescript": "^3.9.5" }, "scripts": { - "format": "git-clang-format" + "format": "git-clang-format", + "postinstall": "ngc -p postinstall.tsconfig.json" }, "husky": { "hooks": { diff --git a/postinstall.tsconfig.json b/postinstall.tsconfig.json new file mode 100644 index 0000000..62d068e --- /dev/null +++ b/postinstall.tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "es2015" + ], + "experimentalDecorators": true, + "types": [], + "module": "amd", + "moduleResolution": "node" + }, + "angularCompilerOptions": { + "enableSummariesForJit": true + }, + "include": [ + "node_modules/@angular/**/*", + "node_modules/@ctrl/**/*", + "node_modules/@ngrx/**/*", + ], + "exclude": [ + "node_modules/@ngrx/store/migrations/**", + "node_modules/@ngrx/store/schematics/**", + "node_modules/@ngrx/store/schematics-core/**", + "node_modules/@ngrx/effects/schematics/**", + "node_modules/@angular/cdk/schematics/**", + "node_modules/@angular/cdk/typings/schematics/**", + "node_modules/@angular/common/upgrade*", + "node_modules/@angular/material/schematics/**", + "node_modules/@angular/material/typings/schematics/**", + "node_modules/@angular/router/upgrade*", + "node_modules/@angular/bazel/**", + "node_modules/@angular/compiler-cli/**", + "node_modules/@angular/**/testing/**", + ] +} \ No newline at end of file diff --git a/src/BUILD.bazel b/src/BUILD.bazel index 16ed414..9b581d0 100644 --- a/src/BUILD.bazel +++ b/src/BUILD.bazel @@ -1,6 +1,11 @@ load("@npm_bazel_typescript//:index.bzl", "ts_library") load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") +exports_files([ + "package.json", + "yarn.lock" +]) + nodejs_binary( name = "change_import_style", data = [ diff --git a/src/change_import_style.ts b/src/change_import_style.ts index e8f3a42..d030da0 100644 --- a/src/change_import_style.ts +++ b/src/change_import_style.ts @@ -19,8 +19,8 @@ function main() { const umdContents = convertToUmd(args, initialContents); fs.writeFileSync(args.output_umd_path, umdContents, 'utf8'); - const commonJsContents = convertToESM(args, initialContents); - fs.writeFileSync(args.output_es6_path, commonJsContents, 'utf8'); + const esmContents = convertToESM(args, initialContents); + fs.writeFileSync(args.output_es6_path, esmContents, 'utf8'); } function replaceRecursiveFilePaths(args: any) { @@ -88,12 +88,18 @@ function convertToESM(args: any, initialContents: string): string { }; const replaceRequiresWithSubpackageImports = (contents: string) => { - return contents.replace(/var ([\w\d_]+) = require\((['"][\w\d@/_-]+['"])\)\.([\w\d_]+);/g, 'import * as $1 from $2;') - } + // Example: + // Changes: var foo = require("@improbable-eng/grpc-web").bar; + // To: import {bar as foo} from "@improbable-eng/grpc-web"; + return contents.replace( + /var ([\w\d_]+) = require\((['"][\w\d@/_-]+['"])\)\.([\w\d_]+);/g, + (_, varName, moduleIdentifier, propertyName) => + `import { ${propertyName} as ${varName} } from ${moduleIdentifier};`); + }; const replaceCJSExportsWithECMAExports = (contents: string) => { return contents.replace(/exports\.([\w\d_]+) = .*;/g, 'export { $1 };') - } + }; const transformations: ((c: string) => string)[] = [ replaceRecursiveFilePaths(args), diff --git a/src/package.json b/src/package.json new file mode 100644 index 0000000..ea41649 --- /dev/null +++ b/src/package.json @@ -0,0 +1,17 @@ +{ + "name": "rules_typescript_proto", + "description": "Bazel rules for generating typescript protocol buffers", + "repository": "https://github.com/Dig-Doug/rules_typescript_proto", + "license": "Apache-2.0", + "dependencies": { + "google-protobuf": "3.11.4" + }, + "devDependencies": { + "@bazel/typescript": "1.4.1", + "@improbable-eng/grpc-web": "0.12.0", + "@types/google-protobuf": "3.7.2", + "minimist": "^1.2.5", + "ts-protoc-gen": "0.12.0", + "typescript": "3.8.3" + } +} diff --git a/src/rules_typescript_proto_dependencies.bzl b/src/rules_typescript_proto_dependencies.bzl index 5257975..e3105ae 100644 --- a/src/rules_typescript_proto_dependencies.bzl +++ b/src/rules_typescript_proto_dependencies.bzl @@ -14,9 +14,9 @@ def rules_typescript_proto_dependencies(): yarn_install( name = "rules_typescript_proto_deps", - package_json = "@rules_typescript_proto//:package.json", + package_json = "@rules_typescript_proto//src:package.json", # Don't use managed directories because these are internal to the library and the # dependencies shouldn't need to be installed by the user. symlink_node_modules = False, - yarn_lock = "@rules_typescript_proto//:yarn.lock", + yarn_lock = "@rules_typescript_proto//src:yarn.lock", ) diff --git a/src/yarn.lock b/src/yarn.lock new file mode 100644 index 0000000..d041e46 --- /dev/null +++ b/src/yarn.lock @@ -0,0 +1,179 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@bazel/typescript@1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.4.1.tgz#1c718ce6de694e1bb90f894fa6320a0cadd90850" + integrity sha512-+R/cZcQpuvGfxqc9s/qu/LBWGNV93iSPTt/mvXq6hfH+sABmrrC/E0+WxapuQaaPdKqR3zSdDIIBV70FUMozsg== + dependencies: + protobufjs "6.8.8" + semver "5.6.0" + source-map-support "0.5.9" + tsutils "2.27.2" + +"@improbable-eng/grpc-web@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.12.0.tgz#9b10a7edf2a1d7672f8997e34a60e7b70e49738f" + integrity sha512-uJjgMPngreRTYPBuo6gswMj1gK39Wbqre/RgE0XnSDXJRg6ST7ZhuS53dFE6Vc2CX4jxgl+cO+0B3op8LA4Q0Q== + dependencies: + browser-headers "^0.4.0" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + +"@types/google-protobuf@3.7.2": + version "3.7.2" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.7.2.tgz#cd8a360c193ce4d672575a20a79f49ba036d38d2" + integrity sha512-ifFemzjNchFBCtHS6bZNhSZCBu7tbtOe0e8qY0z2J4HtFXmPJjm6fXSaQsTG7yhShBEZtt2oP/bkwu5k+emlkQ== + +"@types/long@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" + integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== + +"@types/node@^10.1.0": + version "10.14.22" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.22.tgz#34bcdf6b6cb5fc0db33d24816ad9d3ece22feea4" + integrity sha512-9taxKC944BqoTVjE+UT3pQH0nHZlTvITwfsOZqyc+R3sfJuxaTtxWjfn1K2UlxyPcKHf0rnaXcVFrS9F9vf0bw== + +browser-headers@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/browser-headers/-/browser-headers-0.4.1.tgz#4308a7ad3b240f4203dbb45acedb38dc2d65dd02" + integrity sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +google-protobuf@3.11.4: + version "3.11.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.11.4.tgz#598ca405a3cfa917a2132994d008b5932ef42014" + integrity sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q== + +google-protobuf@^3.6.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.10.0.tgz#f36171128090615049f9b0e42252b1a10a4bc534" + integrity sha512-d0cMO8TJ6xtB/WrVHCv5U81L2ulX+aCD58IljyAN6mHwdHHJ2jbcauX5glvivi3s3hx7EYEo7eUA9WftzamMnw== + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +protobufjs@6.8.8: + version "6.8.8" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" + integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + +semver@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== + +source-map-support@0.5.9: + version "0.5.9" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" + integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +ts-protoc-gen@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.12.0.tgz#932e5738f14b67e7202825b06f8c548cb7d8ef34" + integrity sha512-V7jnICJxKqalBrnJSMTW5tB9sGi48gOC325bfcM7TDNUItVOlaMM//rQmuo49ybipk/SyJTnWXgtJnhHCevNJw== + dependencies: + google-protobuf "^3.6.1" + +tslib@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tsutils@2.27.2: + version "2.27.2" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7" + integrity sha512-qf6rmT84TFMuxAKez2pIfR8UCai49iQsfB7YWVjV1bKpy/d0PWT5rEOSM6La9PiHZ0k1RRZQiwVdVJfQ3BPHgg== + dependencies: + tslib "^1.8.1" + +typescript@3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== diff --git a/test/BUILD.bazel b/test/BUILD.bazel index 2b905b4..efde633 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -106,13 +106,13 @@ rollup_bundle( entry_points = { ":test_bundling.ts": "index", }, + format = "umd", output_dir = True, deps = [ ":test_bundling_lib", - "@npm//rollup-plugin-commonjs", - "@npm//rollup-plugin-node-resolve", + "@npm//@rollup/plugin-commonjs", + "@npm//@rollup/plugin-node-resolve", ], - format = "cjs", ) ts_library( @@ -121,20 +121,54 @@ ts_library( tsconfig = ":test_bundling_tsconfig.json", deps = [ "//test/proto:naming_styles_ts_proto", - "//test/proto/common:pizza_ts_proto", "//test/proto:pizza_service_ts_proto", "//test/proto/common:delivery_person_ts_proto", - "@npm//google-protobuf", + "//test/proto/common:pizza_ts_proto", "@npm//@improbable-eng/grpc-web", + "@npm//google-protobuf", ], ) jasmine_node_test( - name = "rollup_test", + name = "rollup_in_node_test", srcs = [ - ":rollup_test.spec.js" + ":rollup_in_node_test.spec.js", ], data = [ - ":test_es6_bundling" - ] + ":test_es6_bundling", + ], +) + +ts_library( + name = "rollup_in_browser_test", + testonly = True, + srcs = [ + "rollup_in_browser_test.spec.ts", + "test_es6_bundling.d.ts", + ], + tsconfig = ":tsconfig.json", + deps = [ + "@npm//@types/jasmine", + ], +) + +karma_web_test_suite( + name = "rollup_browser_test_suite", + srcs = [ + "require.config.js", + ], + browsers = [ + "@io_bazel_rules_webtesting//browsers:chromium-local", + ], + # Files are served under /base// + static_files = [ + "@npm//:node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.umd.js", + "@npm//:node_modules/browser-headers/dist/browser-headers.umd.js", + ":test_es6_bundling", + ], + tags = ["native"], + deps = [ + ":rollup_in_browser_test", + ":test_es6_bundling", + ], ) diff --git a/test/integration/BUILD.bazel b/test/integration/BUILD.bazel new file mode 100644 index 0000000..496a141 --- /dev/null +++ b/test/integration/BUILD.bazel @@ -0,0 +1,43 @@ +load("@rules_java//java:defs.bzl", "java_library") +load("@io_bazel_rules_webtesting//web:java.bzl", "java_web_test_suite") + +java_web_test_suite( + name = "integration_test", + size = "medium", + browsers = [ + "@io_bazel_rules_webtesting//browsers:chromium-local", + ], + tags = [ + "native", + ], + test_class = "test.integration.IntegrationTest", + runtime_deps = [ + ":IntegrationTest", + ], +) + +java_library( + name = "IntegrationTest", + testonly = True, + srcs = ["IntegrationTest.java"], + data = [ + ":envoy.yaml", + "//test/integration/client:prodserver", + ], + runtime_deps = [ + "@io_grpc_grpc_java//netty", + ], + deps = [ + "//test/integration/proto:service_java_grpc_proto", + "//test/integration/proto:service_java_proto", + "@bazel_tools//tools/java/runfiles", + "@com_google_protobuf//:protobuf_java", + "@io_bazel_rules_webtesting//java/com/google/testing/web", + "@io_grpc_grpc_java//core", + "@io_grpc_grpc_java//stub", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_seleniumhq_selenium_selenium_api", + "@maven//:org_seleniumhq_selenium_selenium_support", + ], +) diff --git a/test/integration/IntegrationTest.java b/test/integration/IntegrationTest.java new file mode 100644 index 0000000..5075819 --- /dev/null +++ b/test/integration/IntegrationTest.java @@ -0,0 +1,183 @@ +package test.integration; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.devtools.build.runfiles.Runfiles; +import com.google.testing.web.WebTest; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.stub.StreamObserver; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.logging.LogEntry; +import org.openqa.selenium.logging.LogType; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import test.integration.proto.Service; +import test.integration.proto.TestServiceGrpc.TestServiceImplBase; + +@RunWith(JUnit4.class) +public class IntegrationTest { + private static final By DO_UNARY_RPC_BUTTON = By.id("do-unary-rpc"); + private static final By RPC_RESPONSE_FIELD = By.id("rpc-response-field"); + private static final String UNARY_RPC_PAYLOAD = "unary-rpc-payload"; + // NOTE: These ports must match the envoy config + private static final int HTTP_SERVER_PORT = 5434; + private static final int GRPC_SERVER_PORT = 19020; + private static final int ENVOY_PROXY_PORT = 10002; + + private static Process httpServerProcess; + private static Process envoyProcess; + private static Server grpcServer; + private static TestServiceImpl grpcServiceImpl; + + private WebDriver driver; + private WebDriverWait wait; + + @BeforeClass + public static void beforeAll() throws Exception { + Runfiles runfiles = Runfiles.create(); + httpServerProcess = startWebServer(runfiles, HTTP_SERVER_PORT); + envoyProcess = startEnvoyProxy(runfiles); + grpcServiceImpl = new TestServiceImpl(); + grpcServer = ServerBuilder.forPort(GRPC_SERVER_PORT).addService(grpcServiceImpl).build(); + grpcServer.start(); + } + + @AfterClass + public static void afterAll() throws Exception { + stopProcess(httpServerProcess); + stopProcess(envoyProcess); + if (grpcServer != null) { + grpcServer.awaitTermination(5, TimeUnit.SECONDS); + } + } + + private static void stopProcess(Process process) { + if (process == null) { + return; + } + process.destroy(); + try { + process.waitFor(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + process.destroyForcibly(); + } + } + + @Before + public void createDriver() { + driver = new WebTest().newWebDriverSession(); + driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); + wait = new WebDriverWait(driver, 1); + + driver.get(String.format("http://localhost:%d", ENVOY_PROXY_PORT)); + } + + @After + public void quitDriver() { + logBrowserLogs(); + try { + driver.quit(); + } finally { + driver = null; + } + + grpcServiceImpl.reset(); + } + + @Test + public void unaryRpc() { + assertThat(grpcServiceImpl.getUnaryCount()).isEqualTo(0); + + wait.until(ExpectedConditions.presenceOfElementLocated(DO_UNARY_RPC_BUTTON)); + getElement(DO_UNARY_RPC_BUTTON).click(); + wait.until(ExpectedConditions.textToBePresentInElement( + getElement(RPC_RESPONSE_FIELD), UNARY_RPC_PAYLOAD)); + + assertThat(grpcServiceImpl.getUnaryCount()).isEqualTo(1); + } + + private WebElement getElement(By by) { + return driver.findElement(by); + } + + private static Process startWebServer(Runfiles runfiles, int serverPort) throws Exception { + String path = + runfiles.rlocation("rules_typescript_proto/test/integration/client/prodserver.sh"); + ProcessBuilder pb = + new ProcessBuilder(path, "--port", Integer.toString(serverPort)).redirectErrorStream(true); + pb.environment().putAll(runfiles.getEnvVars()); + Process p = pb.start(); + waitForReadyMessage(p, "listening on"); + return p; + } + + private static Process startEnvoyProxy(Runfiles runfiles) throws Exception { + String path = runfiles.rlocation("rules_typescript_proto/test/integration/envoy.yaml"); + ProcessBuilder pb = + new ProcessBuilder("envoy", "--config-path", path).redirectErrorStream(true); + pb.environment().putAll(runfiles.getEnvVars()); + Process p = pb.start(); + waitForReadyMessage(p, "starting main dispatch loop"); + return p; + } + + private static void waitForReadyMessage(Process p, String readyMessage) throws Exception { + InputStream is = p.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + while ((line = br.readLine()) != null) { + System.err.println(line); + if (line.contains(readyMessage)) { + break; + } + } + } + + private void logBrowserLogs() { + LogEntries logEntries = driver.manage().logs().get(LogType.BROWSER); + for (LogEntry entry : logEntries) { + System.err.println(String.format("BROWSER: %s %s %s", new Date(entry.getTimestamp()), + entry.getLevel(), entry.getMessage())); + } + } + + private static class TestServiceImpl extends TestServiceImplBase { + private final AtomicLong unaryCount = new AtomicLong(0); + + public long getUnaryCount() { + return unaryCount.get(); + } + + public void reset() { + unaryCount.set(0); + } + + @Override + public void unaryRpc( + Service.TestRequest request, StreamObserver responseObserver) { + unaryCount.incrementAndGet(); + + responseObserver.onNext( + Service.TestResponse.newBuilder().setPayload(UNARY_RPC_PAYLOAD).build()); + responseObserver.onCompleted(); + } + } +} diff --git a/test/integration/client/BUILD.bazel b/test/integration/client/BUILD.bazel new file mode 100644 index 0000000..946b663 --- /dev/null +++ b/test/integration/client/BUILD.bazel @@ -0,0 +1,180 @@ +load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") +load("@npm//@babel/cli:index.bzl", "babel") +load("@npm_bazel_terser//:index.bzl", "terser_minified") +load("@npm//history-server:index.bzl", "history_server") +load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web") +load("@npm_angular_bazel//:index.bzl", "ng_module") +load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("@npm_bazel_typescript//:index.bzl", "ts_config", "ts_devserver", "ts_library") +load("@npm//html-insert-assets:index.bzl", "html_insert_assets") + +package( + default_visibility = ["//test/integration:__subpackages__"], +) + +filegroup( + name = "rxjs_umd_modules", + srcs = [ + ":rxjs_shims.js", + "@npm//:node_modules/rxjs/bundles/rxjs.umd.js", + ], +) + +ng_module( + name = "app", + srcs = [ + "app.component.ts", + "app.module.ts", + ], + assets = [ + "app.component.html", + ], + tsconfig = "//:tsconfig", + deps = [ + "//test/integration/proto:service_ts_proto", + "@npm//@angular/animations", + "@npm//@angular/core", + "@npm//@angular/platform-browser", + "@npm//@improbable-eng/grpc-web", + ], +) + +ng_module( + name = "main", + srcs = [ + "main.dev.ts", + "main.prod.ts", + ], + tsconfig = "//:tsconfig", + deps = [ + ":app", + "@npm//@angular/core", + "@npm//@angular/platform-browser", + ], +) + +sass_binary( + name = "index-styles", + src = "index.scss", +) + +_ASSETS = [ + ":index.css", + "@npm//:node_modules/zone.js/dist/zone.min.js", +] + +html_insert_assets( + name = "inject_scripts_for_dev", + outs = ["index.html"], + args = [ + "--html=$(location :index.dev.html)", + "--out=$@", + "--roots=. $(RULEDIR)", + "--assets", + ] + ["$(location %s)" % s for s in _ASSETS] + [ + # This file doesn't exist during the build, but will be served by ts_devserver + "_/ts_scripts.js", + ], + data = [":index.dev.html"] + _ASSETS, +) + +ts_devserver( + name = "devserver", + entry_module = "rules_typescript_proto/test/integration/client/main.dev", + scripts = [ + "@npm//:node_modules/tslib/tslib.js", + ":rxjs_umd_modules", + ":require.config.js", + ], + static_files = _ASSETS + [ + ":inject_scripts_for_dev", + "@npm//:node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.umd.js", + "@npm//:node_modules/browser-headers/dist/browser-headers.umd.js", + "@npm//google-protobuf:google-protobuf__umd", + ], + deps = [":main"], +) + +rollup_bundle( + name = "bundle-es2015", + config_file = "rollup.config.js", + entry_points = { + ":main.prod.ts": "index", + }, + output_dir = True, + deps = [ + ":main", + "@npm//@improbable-eng/grpc-web", + "@npm//@rollup/plugin-commonjs", + "@npm//@rollup/plugin-node-resolve", + ], +) + +babel( + name = "bundle-es5", + args = [ + "$(location :bundle-es2015)", + "--no-babelrc", + #"--source-maps", + "--presets=@babel/preset-env", + "--out-dir", + "$(@D)", + ], + data = [ + ":bundle-es2015", + "@npm//@babel/preset-env", + ], + output_dir = True, +) + +terser_minified( + name = "bundle-es2015.min", + src = ":bundle-es2015", +) + +terser_minified( + name = "bundle-es5.min", + src = ":bundle-es5", +) + +html_insert_assets( + name = "inject_scripts_for_prod", + # we can't output "src/example/index.html" since that collides with the devmode file. + # pkg_web rule will re-root paths that start with _{name} by default + # so we output "_prodapp/src/example/index.html" so that it is mapped to + # `example/index.html` in the web package. + outs = ["_prodapp/test/integration/client/index.html"], + args = [ + "--html=$(location :index.prod.html)", + "--out=$@", + "--roots=. $(RULEDIR)", + "--assets", + ] + ["$(location %s)" % s for s in _ASSETS], + data = [":index.prod.html"] + _ASSETS, +) + +pkg_web( + name = "prodapp", + # do not sort + srcs = _ASSETS + [ + ":bundle-es2015.min", + ":bundle-es5.min", + ":inject_scripts_for_prod", + # Include polyfills that will be requested by old browsers + "@npm//:node_modules/systemjs/dist/system.js", + "@npm//:node_modules/core-js/client/core.min.js", + ], + additional_root_paths = [ + "npm/node_modules/core-js/client", + "npm/node_modules/systemjs/dist", + ], +) + +history_server( + name = "prodserver", + data = [":prodapp"], + templated_args = [ + "-a", + "test/integration/client/prodapp", + ], +) diff --git a/test/integration/client/app.component.html b/test/integration/client/app.component.html new file mode 100644 index 0000000..b479726 --- /dev/null +++ b/test/integration/client/app.component.html @@ -0,0 +1,9 @@ +

+ rules_typescript_proto Integration Test +

+ + + +
{{rpcResponse}}
\ No newline at end of file diff --git a/test/integration/client/app.component.ts b/test/integration/client/app.component.ts new file mode 100644 index 0000000..ebcccdc --- /dev/null +++ b/test/integration/client/app.component.ts @@ -0,0 +1,32 @@ +import {Component, ElementRef, ViewChild} from '@angular/core'; +import {grpc} from '@improbable-eng/grpc-web'; +import {TestRequest, TestResponse} from 'rules_typescript_proto/test/integration/proto/service_pb'; +import {TestServiceClient} from 'rules_typescript_proto/test/integration/proto/service_pb_service'; + +@Component({selector: 'app-component', templateUrl: 'app.component.html'}) +export class AppComponent { + rpcResponse: string; + + doUnaryRpc() { + const client = new TestServiceClient('/_/api', {transport: grpc.CrossBrowserHttpTransport({})}); + + const req = new TestRequest(); + const responsePromise = new Promise((resolve, reject) => { + client.unaryRpc(req, undefined, (err, response) => { + if (err) { + reject(err); + } else { + resolve(response); + } + }); + }); + + responsePromise + .then((response: any) => { + this.rpcResponse = response.getPayload(); + }) + .catch((e: Error) => { + this.rpcResponse = e.stack; + }) + } +} diff --git a/test/integration/client/app.module.ts b/test/integration/client/app.module.ts new file mode 100644 index 0000000..d8f9428 --- /dev/null +++ b/test/integration/client/app.module.ts @@ -0,0 +1,16 @@ +import {NgModule} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {AppComponent} from './app.component'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + BrowserModule, + BrowserAnimationsModule, + ], + exports: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule { +} diff --git a/test/integration/client/index.dev.html b/test/integration/client/index.dev.html new file mode 100644 index 0000000..c55e3e8 --- /dev/null +++ b/test/integration/client/index.dev.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/integration/client/index.prod.html b/test/integration/client/index.prod.html new file mode 100644 index 0000000..b73e685 --- /dev/null +++ b/test/integration/client/index.prod.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/test/integration/client/index.scss b/test/integration/client/index.scss new file mode 100644 index 0000000..b6e2d6d --- /dev/null +++ b/test/integration/client/index.scss @@ -0,0 +1,63 @@ +html { + height: 100%; + width: 100%; + + body { + height: 100%; + margin: 0; + width: 100%; + } +} + +/* Reset css TODO(doug) */ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, +acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, +sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, +caption tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, +figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ + +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { + display: block; +} + +body { + line-height: 1; +} + +ol, ul { + list-style: none; +} + +blockquote, q { + quotes: none; +} + +blockquote { + &:before, &:after { + content: ''; + content: none; + } +} + +q { + &:before, &:after { + content: ''; + content: none; + } +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + diff --git a/test/integration/client/main.dev.ts b/test/integration/client/main.dev.ts new file mode 100644 index 0000000..eae4a02 --- /dev/null +++ b/test/integration/client/main.dev.ts @@ -0,0 +1,7 @@ +/** + * Used to launch the application under Bazel development mode. + */ +import {platformBrowser} from '@angular/platform-browser'; +import {AppModuleNgFactory} from './app.module.ngfactory'; + +platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); diff --git a/test/integration/client/main.prod.ts b/test/integration/client/main.prod.ts new file mode 100644 index 0000000..52ab460 --- /dev/null +++ b/test/integration/client/main.prod.ts @@ -0,0 +1,9 @@ +/** + * Used to launch the application under Bazel production mode. + */ +import {enableProdMode} from '@angular/core'; +import {platformBrowser} from '@angular/platform-browser'; +import {AppModuleNgFactory} from './app.module.ngfactory'; + +enableProdMode(); +platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); diff --git a/test/integration/client/require.config.js b/test/integration/client/require.config.js new file mode 100644 index 0000000..1426508 --- /dev/null +++ b/test/integration/client/require.config.js @@ -0,0 +1,8 @@ +require.config({ + paths: { + '@improbable-eng/grpc-web': + 'npm/node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.umd', + 'browser-headers': 'npm/node_modules/browser-headers/dist/browser-headers.umd', + 'google-protobuf': 'npm/google-protobuf/google-protobuf.umd', + }, +}); diff --git a/test/integration/client/rollup.config.js b/test/integration/client/rollup.config.js new file mode 100644 index 0000000..6ef4755 --- /dev/null +++ b/test/integration/client/rollup.config.js @@ -0,0 +1,17 @@ +const node = require('@rollup/plugin-node-resolve'); +const commonjs = require('@rollup/plugin-commonjs'); + +module.exports = { + plugins: [ + node({ + mainFields: ['browser', 'es2015', 'module', 'jsnext:main', 'main'], + }), + commonjs({ + namedExports: { + 'node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.umd.js': [ + 'grpc', + ], + }, + }), + ], +}; diff --git a/test/integration/client/rxjs_shims.js b/test/integration/client/rxjs_shims.js new file mode 100644 index 0000000..049bb5d --- /dev/null +++ b/test/integration/client/rxjs_shims.js @@ -0,0 +1,46 @@ +/** + * @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 these provide named UMD modules so that we can bundle + * the application along with rxjs using the concatjs bundler. + */ + +// rxjs/operators +(function(factory) { +if (typeof module === 'object' && typeof module.exports === 'object') { + var v = factory(require, exports); + if (v !== undefined) + module.exports = v; +} else if (typeof define === 'function' && define.amd) { + define('rxjs/operators', ['exports', 'rxjs'], factory); +} +})(function(exports, rxjs) { +'use strict'; +Object.keys(rxjs.operators).forEach(function(key) { + exports[key] = rxjs.operators[key]; +}); +Object.defineProperty(exports, '__esModule', {value: true}); +}); + +// rxjs/testing +(function(factory) { +if (typeof module === 'object' && typeof module.exports === 'object') { + var v = factory(require, exports); + if (v !== undefined) + module.exports = v; +} else if (typeof define === 'function' && define.amd) { + define('rxjs/testing', ['exports', 'rxjs'], factory); +} +})(function(exports, rxjs) { +'use strict'; +Object.keys(rxjs.testing).forEach(function(key) { + exports[key] = rxjs.testing[key]; +}); +Object.defineProperty(exports, '__esModule', {value: true}); +}); \ No newline at end of file diff --git a/test/integration/envoy.yaml b/test/integration/envoy.yaml new file mode 100644 index 0000000..3463154 --- /dev/null +++ b/test/integration/envoy.yaml @@ -0,0 +1,69 @@ +admin: + access_log_path: /tmp/admin_access.log + address: + socket_address: { address: 0.0.0.0, port_value: 9901 } +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + address: 0.0.0.0 + port_value: 10002 + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager + stat_prefix: ingress_http + codec_type: AUTO + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: + prefix: "/_/api/" + route: + prefix_rewrite: "/" + cluster: grpc_service + max_grpc_timeout: 0s + - match: + prefix: "/" + route: + cluster: web_client + http_filters: + - name: envoy.grpc_web + - name: envoy.router + clusters: + - name: web_client + connect_timeout: 0.25s + type: LOGICAL_DNS + # Comment out the following line to test on v6 networks + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: web_client + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 5434 + - name: grpc_service + connect_timeout: 0.25s + type: LOGICAL_DNS + # Comment out the following line to test on v6 networks + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: grpc_service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 19020 diff --git a/test/integration/proto/BUILD.bazel b/test/integration/proto/BUILD.bazel new file mode 100644 index 0000000..d015b25 --- /dev/null +++ b/test/integration/proto/BUILD.bazel @@ -0,0 +1,35 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_java//java:defs.bzl", "java_proto_library") +load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library") +load("@rules_typescript_proto//:index.bzl", "typescript_proto_library") + +package(default_visibility = ["//test/integration:__subpackages__"]) + +proto_library( + name = "service_proto", + srcs = [ + "service.proto", + ], +) + +java_proto_library( + name = "service_java_proto", + deps = [ + ":service_proto", + ], +) + +java_grpc_library( + name = "service_java_grpc_proto", + srcs = [ + ":service_proto", + ], + deps = [ + ":service_java_proto", + ], +) + +typescript_proto_library( + name = "service_ts_proto", + proto = ":service_proto", +) diff --git a/test/integration/proto/service.proto b/test/integration/proto/service.proto new file mode 100644 index 0000000..6c6f50b --- /dev/null +++ b/test/integration/proto/service.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package test.integration.proto; + +service TestService { + rpc UnaryRpc(TestRequest) returns (TestResponse) { + } +} + +message TestRequest { +} + +message TestResponse { + string payload = 1; +} diff --git a/test/pizza_service_proto_test.spec.ts b/test/pizza_service_proto_test.spec.ts index 4584fab..4feadce 100644 --- a/test/pizza_service_proto_test.spec.ts +++ b/test/pizza_service_proto_test.spec.ts @@ -1,6 +1,6 @@ import {Pizza, PizzaSize} from 'rules_typescript_proto/test/proto/common/pizza_pb'; import {OrderPizzaRequest, OrderPizzaResponse} from 'rules_typescript_proto/test/proto/pizza_service_pb'; -import {PizzaService} from 'rules_typescript_proto/test/proto/pizza_service_pb_service'; +import {PizzaService, PizzaServiceClient} from 'rules_typescript_proto/test/proto/pizza_service_pb_service'; declare function require(module: string): any; @@ -14,6 +14,12 @@ describe('DeliveryPerson', () => { expect(PizzaService).toBeDefined(); }); + it('PizzaServiceClient.orderPizza should return a UnaryResponse', () => { + const client = new PizzaServiceClient('http://localhost', {}); + const response = client.orderPizza(new OrderPizzaRequest(), (_) => {}); + expect(typeof response.cancel).toBe('function'); + }); + it('Generated code seems to work', () => { const request = new OrderPizzaRequest(); diff --git a/test/require.config.js b/test/require.config.js index 0c60723..de3649c 100644 --- a/test/require.config.js +++ b/test/require.config.js @@ -2,6 +2,7 @@ require.config({ paths: { '@improbable-eng/grpc-web': 'base/npm/node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.umd', - 'browser-headers': 'base/npm/node_modules/browser-headers/dist/browser-headers.umd' + 'browser-headers': 'base/npm/node_modules/browser-headers/dist/browser-headers.umd', + 'test_es6_bundling': 'base/rules_typescript_proto/test/test_es6_bundling/index', } }); diff --git a/test/rollup.config.js b/test/rollup.config.js index 89a556a..b9a97e5 100644 --- a/test/rollup.config.js +++ b/test/rollup.config.js @@ -1,16 +1,19 @@ -const commonjs = require('rollup-plugin-commonjs'); -const nodeRequire = require('rollup-plugin-node-resolve'); +const commonjs = require('@rollup/plugin-commonjs'); +const nodeRequire = require('@rollup/plugin-node-resolve'); module.exports = { + output: { + name: 'test_es6_bundling', + }, plugins: [ nodeRequire(), commonjs({ // Temporary fix until https://github.com/improbable-eng/grpc-web/issues/369 is resolved. namedExports: { - './node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.js': [ + 'node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.js': [ 'grpc', ], - } + }, }), ], }; diff --git a/test/rollup_in_browser_test.spec.ts b/test/rollup_in_browser_test.spec.ts new file mode 100644 index 0000000..e69c08a --- /dev/null +++ b/test/rollup_in_browser_test.spec.ts @@ -0,0 +1,47 @@ +import * as bundle from 'test_es6_bundling'; + +describe('Rollup', () => { + it('should define Pizza with protobuf API', () => { + expect(bundle.Pizza).toBeDefined(); + + const pizza = new bundle.Pizza(); + pizza.setSize(bundle.PizzaSize.PIZZA_SIZE_LARGE); + + expect(pizza.getSize()).toBe(bundle.PizzaSize.PIZZA_SIZE_LARGE); + expect(Array.isArray(pizza.getToppingIdsList())).toBe(true); + }); + + it('should define DeliveryPerson', () => { + expect(bundle.DeliveryPerson).toBeDefined(); + expect(new bundle.DeliveryPerson()).toBeTruthy(); + }); + + it('should define PizzaService', () => { + expect(bundle.PizzaService).toBeDefined(); + expect(bundle.PizzaService.serviceName).toBe('test.bazel.proto.PizzaService'); + expect(bundle.PizzaService.OrderPizza.methodName).toBe('OrderPizza'); + }); + + it('should define PizzaServiceClient', () => { + expect(bundle.PizzaServiceClient).toBeDefined(); + const client = new bundle.PizzaServiceClient('http://localhost', {}); + expect(typeof client.orderPizza).toBe('function'); + }); + + it('should follow expected naming styles', () => { + expect(new bundle.alllowercase().setTest(1)).toBeTruthy(); + expect(new bundle.ALLUPPERCASE().setTest(1)).toBeTruthy(); + expect(new bundle.lowerCamelCase().setTest(1)).toBeTruthy(); + expect(new bundle.UpperCamelCase().setTest(1)).toBeTruthy(); + expect(new bundle.snake_case_snake_case().setTest(1)).toBeTruthy(); + expect(new bundle.Upper_snake_Case().setTest(1)).toBeTruthy(); + expect(new bundle.M2M().setTest(1)).toBeTruthy(); + expect(new bundle.M_2M().setTest(1)).toBeTruthy(); + expect(new bundle.M2_M().setTest(1)).toBeTruthy(); + expect(new bundle.M2M_().setTest(1)).toBeTruthy(); + expect(new bundle.m_22M().setTest(1)).toBeTruthy(); + expect(new bundle.m42_M().setTest(1)).toBeTruthy(); + expect(new bundle.m24M_().setTest(1)).toBeTruthy(); + expect(new bundle.M9().setTest(1)).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/test/rollup_test.spec.js b/test/rollup_in_node_test.spec.js similarity index 100% rename from test/rollup_test.spec.js rename to test/rollup_in_node_test.spec.js diff --git a/test/test_es6_bundling.d.ts b/test/test_es6_bundling.d.ts new file mode 100644 index 0000000..d842658 --- /dev/null +++ b/test/test_es6_bundling.d.ts @@ -0,0 +1,4 @@ +declare module 'test_es6_bundling' { + var a: any; + export = a; +} \ No newline at end of file