mirror of https://github.com/grpc/grpc-web.git
Revamp Closure JsUnit tests runtime and optimize test/build flows. (#1137)
This commit is contained in:
parent
d9a6c7a738
commit
32fe12459b
|
|
@ -0,0 +1,2 @@
|
|||
**/node_modules
|
||||
packages/grpc-web/generated
|
||||
|
|
@ -1,16 +1,9 @@
|
|||
version: '3'
|
||||
services:
|
||||
common:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./net/grpc/gateway/docker/common/Dockerfile
|
||||
image: grpcweb/common
|
||||
prereqs:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./net/grpc/gateway/docker/prereqs/Dockerfile
|
||||
depends_on:
|
||||
- common
|
||||
image: grpcweb/prereqs
|
||||
echo-server:
|
||||
build:
|
||||
|
|
@ -34,8 +27,6 @@ services:
|
|||
build:
|
||||
context: ./
|
||||
dockerfile: ./net/grpc/gateway/docker/node_interop_server/Dockerfile
|
||||
depends_on:
|
||||
- common
|
||||
image: grpcweb/node-interop-server
|
||||
ports:
|
||||
- "7074:7074"
|
||||
|
|
@ -114,3 +105,8 @@ services:
|
|||
depends_on:
|
||||
- prereqs
|
||||
image: grpcweb/protoc-plugin
|
||||
jsunit-test:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./packages/grpc-web/docker/jsunit-test/Dockerfile
|
||||
image: grpcweb/jsunit-test
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library", "closure_js_test")
|
||||
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||
|
||||
cc_binary(
|
||||
|
|
@ -239,49 +239,3 @@ closure_js_library(
|
|||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
closure_js_test(
|
||||
name = "grpcwebclientbase_test",
|
||||
srcs = [
|
||||
"grpcwebclientbase_test.js",
|
||||
],
|
||||
entry_points = [
|
||||
"goog:grpc.web.GrpcWebClientBaseTest",
|
||||
],
|
||||
suppress = [
|
||||
"visibility",
|
||||
"checkTypes",
|
||||
"deprecated",
|
||||
"reportUnknownTypes",
|
||||
"strictCheckTypes",
|
||||
],
|
||||
deps = [
|
||||
":clientreadablestream",
|
||||
":grpcwebclientbase",
|
||||
":interceptor",
|
||||
"@io_bazel_rules_closure//closure/library/crypt:base64",
|
||||
"@io_bazel_rules_closure//closure/library/events",
|
||||
"@io_bazel_rules_closure//closure/library/net:eventtype",
|
||||
"@io_bazel_rules_closure//closure/library/structs:map",
|
||||
"@io_bazel_rules_closure//closure/library/testing:jsunit",
|
||||
"@io_bazel_rules_closure//closure/library/testing:testsuite",
|
||||
],
|
||||
)
|
||||
|
||||
closure_js_test(
|
||||
name = "grpcwebstreamparser_test",
|
||||
srcs = [
|
||||
"grpcwebstreamparser_test.js",
|
||||
],
|
||||
entry_points = [
|
||||
"goog:grpc.web.GrpcWebStreamParserTest",
|
||||
],
|
||||
suppress = [
|
||||
"reportUnknownTypes",
|
||||
],
|
||||
deps = [
|
||||
":grpcwebstreamparser",
|
||||
"@io_bazel_rules_closure//closure/library/testing:jsunit",
|
||||
"@io_bazel_rules_closure//closure/library/testing:testsuite",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -239,6 +239,13 @@ class MockXhr {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} ms
|
||||
*/
|
||||
setTimeoutInterval(ms) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} withCredentials
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,445 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @fileoverview gRPC web client Readable Stream
|
||||
*
|
||||
* This class is being returned after a gRPC streaming call has been
|
||||
* started. This class provides functionality for user to operates on
|
||||
* the stream, e.g. set onData callback, etc.
|
||||
*
|
||||
* This wraps the underlying goog.net.streams.NodeReadableStream
|
||||
*
|
||||
* @author stanleycheung@google.com (Stanley Cheung)
|
||||
*/
|
||||
goog.module('grpc.web.StreamBodyClientReadableStream');
|
||||
|
||||
goog.module.declareLegacyNamespace();
|
||||
|
||||
|
||||
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
|
||||
const ErrorCode = goog.require('goog.net.ErrorCode');
|
||||
const EventType = goog.require('goog.net.EventType');
|
||||
const GoogleRpcStatus = goog.require('proto.google.rpc.Status');
|
||||
const GrpcWebError = goog.requireType('grpc.web.Error');
|
||||
const Metadata = goog.requireType('grpc.web.Metadata');
|
||||
const NodeReadableStream = goog.require('goog.net.streams.NodeReadableStream');
|
||||
const StatusCode = goog.require('grpc.web.StatusCode');
|
||||
const XhrIo = goog.require('goog.net.XhrIo');
|
||||
const asserts = goog.require('goog.asserts');
|
||||
const events = goog.require('goog.events');
|
||||
const {GenericTransportInterface} = goog.require('grpc.web.GenericTransportInterface');
|
||||
const {Status} = goog.require('grpc.web.Status');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A stream that the client can read from. Used for calls that are streaming
|
||||
* from the server side.
|
||||
* @template RESPONSE
|
||||
* @implements {ClientReadableStream}
|
||||
* @final
|
||||
* @unrestricted
|
||||
*/
|
||||
class StreamBodyClientReadableStream {
|
||||
/**
|
||||
* @param {!GenericTransportInterface} genericTransportInterface
|
||||
* @param {function(?): RESPONSE} responseDeserializeFn
|
||||
* @param {boolean} isBinary
|
||||
*/
|
||||
constructor(genericTransportInterface, responseDeserializeFn, isBinary) {
|
||||
/**
|
||||
* Whether or not the response protobuffer format is binary.
|
||||
* @private
|
||||
* @const {boolean}
|
||||
*/
|
||||
this.isBinary_ = isBinary;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {?NodeReadableStream|undefined} The XHR Node Readable Stream
|
||||
*/
|
||||
this.xhrNodeReadableStream_ = genericTransportInterface.nodeReadableStream;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {function(?): RESPONSE} The deserialize function for the proto
|
||||
*/
|
||||
this.grpcResponseDeserializeFn_ = responseDeserializeFn;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {?XhrIo|undefined} The XhrIo object
|
||||
*/
|
||||
this.xhr_ = genericTransportInterface.xhr;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {!Array<function(RESPONSE)>} The list of data callback
|
||||
*/
|
||||
this.onDataCallbacks_ = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {!Array<function(!Metadata)>} The list of metadata callbacks
|
||||
*/
|
||||
this.onMetadataCallbacks_ = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {!Array<function(!Status)>} The list of status callback
|
||||
*/
|
||||
this.onStatusCallbacks_ = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {!Array<function(...)>} The list of stream end callback
|
||||
*/
|
||||
this.onEndCallbacks_ = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @const {!Array<function(!GrpcWebError)>} The list of error callback
|
||||
*/
|
||||
this.onErrorCallbacks_ = [];
|
||||
|
||||
if (this.xhrNodeReadableStream_) {
|
||||
this.setStreamCallback_();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set up the callback functions for unary calls.
|
||||
* @param {boolean} base64Encoded True if
|
||||
* 'X-Goog-Encode-Response-If-Executable' is 'base64' in request headers.
|
||||
*/
|
||||
setUnaryCallback(base64Encoded) {
|
||||
events.listen(/** @type {!XhrIo}*/ (this.xhr_), EventType.COMPLETE, (e) => {
|
||||
if (this.xhr_.isSuccess()) {
|
||||
let response;
|
||||
if (this.isBinary_) {
|
||||
response = this.decodeBinaryResponse_(base64Encoded);
|
||||
} else {
|
||||
response = this.decodeJspbResponse_(base64Encoded);
|
||||
}
|
||||
const responseMessage = this.grpcResponseDeserializeFn_(response);
|
||||
const grpcStatus = StatusCode.fromHttpStatus(this.xhr_.getStatus());
|
||||
this.sendMetadataCallbacks_(this.readHeaders_());
|
||||
if (grpcStatus == StatusCode.OK) {
|
||||
this.sendDataCallbacks_(responseMessage);
|
||||
} else {
|
||||
this.sendErrorCallbacks_(
|
||||
/** @type {!GrpcWebError} */ (
|
||||
{code: grpcStatus, message: response}));
|
||||
}
|
||||
} else {
|
||||
let rawResponse;
|
||||
if (this.isBinary_) {
|
||||
const xhrResponse = this.xhr_.getResponse();
|
||||
if (xhrResponse) {
|
||||
rawResponse =
|
||||
new Uint8Array(/** @type {!ArrayBuffer} */ (xhrResponse));
|
||||
}
|
||||
} else {
|
||||
rawResponse = this.xhr_.getResponseText();
|
||||
}
|
||||
|
||||
let code;
|
||||
let message;
|
||||
let metadata = {};
|
||||
if (rawResponse) {
|
||||
const status = this.parseRpcStatus_(rawResponse);
|
||||
code = status.code;
|
||||
message = status.details;
|
||||
metadata = status.metadata;
|
||||
} else {
|
||||
code = StatusCode.UNKNOWN;
|
||||
message = 'Rpc failed due to xhr error. error code: ' +
|
||||
this.xhr_.getLastErrorCode() +
|
||||
', error: ' + this.xhr_.getLastError();
|
||||
}
|
||||
this.sendMetadataCallbacks_(this.readHeaders_());
|
||||
this.sendErrorCallbacks_({code, message, metadata});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setStreamCallback_() {
|
||||
// Add the callback to the underlying stream
|
||||
this.xhrNodeReadableStream_.on('data', (data) => {
|
||||
if ('1' in data) {
|
||||
const response = this.grpcResponseDeserializeFn_(data['1']);
|
||||
this.sendDataCallbacks_(response);
|
||||
}
|
||||
if ('2' in data) {
|
||||
const status = this.parseRpcStatus_(data['2']);
|
||||
this.sendStatusCallbacks_(status);
|
||||
}
|
||||
});
|
||||
this.xhrNodeReadableStream_.on('end', () => {
|
||||
this.sendMetadataCallbacks_(this.readHeaders_());
|
||||
this.sendEndCallbacks_();
|
||||
});
|
||||
this.xhrNodeReadableStream_.on('error', () => {
|
||||
if (this.onErrorCallbacks_.length == 0) return;
|
||||
let lastErrorCode = this.xhr_.getLastErrorCode();
|
||||
if (lastErrorCode === ErrorCode.NO_ERROR && !this.xhr_.isSuccess()) {
|
||||
// The lastErrorCode on the XHR isn't useful in this case, but the XHR
|
||||
// status is. Full details about the failure should be available in the
|
||||
// status handler.
|
||||
lastErrorCode = ErrorCode.HTTP_ERROR;
|
||||
}
|
||||
|
||||
let grpcStatusCode;
|
||||
switch (lastErrorCode) {
|
||||
case ErrorCode.NO_ERROR:
|
||||
grpcStatusCode = StatusCode.UNKNOWN;
|
||||
break;
|
||||
case ErrorCode.ABORT:
|
||||
grpcStatusCode = StatusCode.ABORTED;
|
||||
break;
|
||||
case ErrorCode.TIMEOUT:
|
||||
grpcStatusCode = StatusCode.DEADLINE_EXCEEDED;
|
||||
break;
|
||||
case ErrorCode.HTTP_ERROR:
|
||||
grpcStatusCode = StatusCode.fromHttpStatus(this.xhr_.getStatus());
|
||||
break;
|
||||
default:
|
||||
grpcStatusCode = StatusCode.UNAVAILABLE;
|
||||
}
|
||||
|
||||
this.sendErrorCallbacks_({
|
||||
code: grpcStatusCode,
|
||||
// TODO(armiller): get the message from the response?
|
||||
// GoogleRpcStatus.deserialize(rawResponse).getMessage()?
|
||||
// Perhaps do the same status logic as in on('data') above?
|
||||
message: ErrorCode.getDebugMessage(lastErrorCode)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {boolean} base64Encoded
|
||||
* @return {string}
|
||||
*/
|
||||
decodeJspbResponse_(base64Encoded) {
|
||||
// If the response is serialized as Base64 (for example if the
|
||||
// X-Goog-Encode-Response-If-Executable header is in effect), decode it
|
||||
// before passing it to the deserializer.
|
||||
let responseText = this.xhr_.getResponseText();
|
||||
if (base64Encoded &&
|
||||
this.xhr_.getResponseHeader(XhrIo.CONTENT_TYPE_HEADER) ===
|
||||
'text/plain') {
|
||||
if (!atob) {
|
||||
throw new Error('Cannot decode Base64 response');
|
||||
}
|
||||
responseText = atob(responseText);
|
||||
}
|
||||
return responseText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {boolean} base64Encoded
|
||||
* @return {*}
|
||||
*/
|
||||
decodeBinaryResponse_(base64Encoded) {
|
||||
if (base64Encoded &&
|
||||
this.xhr_.getResponseHeader('X-Goog-Safety-Encoding') == 'base64') {
|
||||
// Convert the response's ArrayBuffer to a string, which should
|
||||
// be a base64 encoded string.
|
||||
const bytes = new Uint8Array(
|
||||
/** @type {!ArrayBuffer} */ (this.xhr_.getResponse()));
|
||||
let byteSource = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
byteSource += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return byteSource;
|
||||
} else {
|
||||
return this.xhr_.getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {!Metadata}
|
||||
*/
|
||||
readHeaders_() {
|
||||
const initialMetadata = {};
|
||||
const responseHeaders = this.xhr_.getResponseHeaders();
|
||||
Object.keys(responseHeaders).forEach((header) => {
|
||||
initialMetadata[header] = responseHeaders[header];
|
||||
});
|
||||
return initialMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {!Uint8Array|string} data Data returned from the underlying stream.
|
||||
* @return {!Status} The Rpc Status details.
|
||||
*/
|
||||
parseRpcStatus_(data) {
|
||||
let code;
|
||||
let message;
|
||||
const metadata = {};
|
||||
try {
|
||||
let rpcStatus;
|
||||
if (this.isBinary_) {
|
||||
rpcStatus = GoogleRpcStatus.deserializeBinary(data);
|
||||
} else {
|
||||
asserts.assertString(
|
||||
data, 'RPC status must be string in gRPC-Web jspb mode.');
|
||||
rpcStatus = GoogleRpcStatus.deserialize(data);
|
||||
}
|
||||
code = rpcStatus.getCode();
|
||||
message = rpcStatus.getMessage();
|
||||
if (rpcStatus.getDetailsList().length) {
|
||||
metadata['grpc-web-status-details-bin'] = data;
|
||||
}
|
||||
} catch (e) {
|
||||
// 404s may be accompanied by a GoogleRpcStatus. If they are not,
|
||||
// the generic message will fail to deserialize because it is not a
|
||||
// status.
|
||||
if (this.xhr_ && this.xhr_.getStatus() === 404) {
|
||||
code = StatusCode.NOT_FOUND;
|
||||
message = 'Not Found: ' + this.xhr_.getLastUri();
|
||||
} else {
|
||||
code = StatusCode.UNAVAILABLE;
|
||||
message = 'Unable to parse RpcStatus: ' + e;
|
||||
}
|
||||
}
|
||||
const status = {code: code, details: message, metadata: metadata};
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
on(eventType, callback) {
|
||||
// TODO(stanleycheung): change eventType to @enum type
|
||||
if (eventType == 'data') {
|
||||
this.onDataCallbacks_.push(callback);
|
||||
} else if (eventType == 'metadata') {
|
||||
this.onMetadataCallbacks_.push(callback);
|
||||
} else if (eventType == 'status') {
|
||||
this.onStatusCallbacks_.push(callback);
|
||||
} else if (eventType == 'end') {
|
||||
this.onEndCallbacks_.push(callback);
|
||||
} else if (eventType == 'error') {
|
||||
this.onErrorCallbacks_.push(callback);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {!Array<function(?)>} callbacks the internal list of callbacks
|
||||
* @param {function(?)} callback the callback to remove
|
||||
*/
|
||||
removeListenerFromCallbacks_(callbacks, callback) {
|
||||
const index = callbacks.indexOf(callback);
|
||||
if (index > -1) {
|
||||
callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @override
|
||||
*/
|
||||
removeListener(eventType, callback) {
|
||||
if (eventType == 'data') {
|
||||
this.removeListenerFromCallbacks_(this.onDataCallbacks_, callback);
|
||||
} else if (eventType == 'metadata') {
|
||||
this.removeListenerFromCallbacks_(this.onMetadataCallbacks_, callback);
|
||||
} else if (eventType == 'status') {
|
||||
this.removeListenerFromCallbacks_(this.onStatusCallbacks_, callback);
|
||||
} else if (eventType == 'end') {
|
||||
this.removeListenerFromCallbacks_(this.onEndCallbacks_, callback);
|
||||
} else if (eventType == 'error') {
|
||||
this.removeListenerFromCallbacks_(this.onErrorCallbacks_, callback);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
cancel() {
|
||||
this.xhr_.abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {!RESPONSE} data The data to send back
|
||||
*/
|
||||
sendDataCallbacks_(data) {
|
||||
for (let i = 0; i < this.onDataCallbacks_.length; i++) {
|
||||
this.onDataCallbacks_[i](data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {!Metadata} metadata The metadata to send back
|
||||
*/
|
||||
sendMetadataCallbacks_(metadata) {
|
||||
for (let i = 0; i < this.onMetadataCallbacks_.length; i++) {
|
||||
this.onMetadataCallbacks_[i](metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {!Status} status The status to send back
|
||||
*/
|
||||
sendStatusCallbacks_(status) {
|
||||
for (let i = 0; i < this.onStatusCallbacks_.length; i++) {
|
||||
this.onStatusCallbacks_[i](status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {!GrpcWebError} error The error to send back
|
||||
*/
|
||||
sendErrorCallbacks_(error) {
|
||||
for (let i = 0; i < this.onErrorCallbacks_.length; i++) {
|
||||
this.onErrorCallbacks_[i](error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
sendEndCallbacks_() {
|
||||
for (let i = 0; i < this.onEndCallbacks_.length; i++) {
|
||||
this.onEndCallbacks_[i]();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports = StreamBodyClientReadableStream;
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM node:12-stretch
|
||||
|
||||
ARG PROTOBUF_VERSION=3.17.3
|
||||
|
||||
RUN apt-get -qq update && apt-get -qq install -y \
|
||||
unzip
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
RUN curl -sSL https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/\
|
||||
protoc-$PROTOBUF_VERSION-linux-x86_64.zip -o protoc.zip && \
|
||||
unzip -qq protoc.zip && \
|
||||
cp ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
RUN curl -sSL https://github.com/grpc/grpc-web/releases/download/1.2.1/\
|
||||
protoc-gen-grpc-web-1.2.1-linux-x86_64 -o /usr/local/bin/protoc-gen-grpc-web && \
|
||||
chmod +x /usr/local/bin/protoc-gen-grpc-web
|
||||
|
||||
WORKDIR /var/www/html/dist
|
||||
|
||||
WORKDIR /github/grpc-web
|
||||
|
||||
RUN git clone https://github.com/grpc/grpc-web .
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM grpcweb/common
|
||||
FROM node:12.22.6-stretch
|
||||
|
||||
WORKDIR /github/grpc-node
|
||||
|
||||
|
|
|
|||
|
|
@ -12,39 +12,54 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM grpcweb/common
|
||||
|
||||
ARG MAKEFLAGS=-j8
|
||||
# Using multi-stage buid (See: https://docs.docker.com/develop/develop-images/multistage-build/)
|
||||
|
||||
######################################
|
||||
# Stage 1: Fetch binaries
|
||||
######################################
|
||||
# node:... Docker image is based on buildpack-deps:stretch
|
||||
FROM buildpack-deps:stretch AS prepare
|
||||
|
||||
ARG BUILDIFIER_VERSION=1.0.0
|
||||
ARG BAZEL_VERSION=4.1.0
|
||||
ARG PROTOBUF_VERSION=3.17.3
|
||||
|
||||
RUN echo "\nloglevel=error\n" >> $HOME/.npmrc
|
||||
RUN apt-get -qq update && apt-get -qq install -y curl unzip
|
||||
|
||||
WORKDIR /github/grpc-web
|
||||
WORKDIR /tmp
|
||||
|
||||
# Check out scripts and initialize git submodules first to take advantage of docker build cache.
|
||||
COPY ./scripts ./scripts
|
||||
RUN ./scripts/init_submodules.sh
|
||||
|
||||
COPY ./packages ./packages
|
||||
RUN cd ./packages/grpc-web && \
|
||||
npm install && \
|
||||
npm run build && \
|
||||
npm link
|
||||
|
||||
COPY ./Makefile ./Makefile
|
||||
COPY ./WORKSPACE ./WORKSPACE
|
||||
COPY ./bazel ./bazel
|
||||
COPY ./javascript ./javascript
|
||||
COPY ./net ./net
|
||||
COPY ./src ./src
|
||||
COPY ./test ./test
|
||||
RUN curl -sSL https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/\
|
||||
protoc-$PROTOBUF_VERSION-linux-x86_64.zip -o protoc.zip && \
|
||||
unzip -qq protoc.zip && \
|
||||
cp ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
RUN wget -nv -O buildifier \
|
||||
https://github.com/bazelbuild/buildtools/releases/download/$BUILDIFIER_VERSION/buildifier && \
|
||||
chmod +x ./buildifier && \
|
||||
./buildifier --mode=check --lint=warn --warnings=all -r ./WORKSPACE bazel javascript net && \
|
||||
rm ./buildifier
|
||||
cp ./buildifier /usr/local/bin/buildifier
|
||||
|
||||
# Download third_party modules to be used for the next stage
|
||||
WORKDIR /github/grpc-web
|
||||
|
||||
RUN git clone https://github.com/grpc/grpc-web .
|
||||
COPY ./scripts/init_submodules.sh ./scripts/
|
||||
RUN ./scripts/init_submodules.sh
|
||||
|
||||
|
||||
######################################
|
||||
# Stage 2: Copy source files and build
|
||||
######################################
|
||||
FROM node:12.22.6-stretch AS copy-and-build
|
||||
|
||||
ARG MAKEFLAGS=-j8
|
||||
ARG BAZEL_VERSION=4.1.0
|
||||
|
||||
RUN mkdir -p /var/www/html/dist
|
||||
RUN echo "\nloglevel=error\n" >> $HOME/.npmrc
|
||||
|
||||
COPY --from=prepare /usr/local/bin/protoc /usr/local/bin/
|
||||
COPY --from=prepare /usr/local/bin/buildifier /usr/local/bin/
|
||||
COPY --from=prepare /github/grpc-web/third_party /github/grpc-web/third_party
|
||||
|
||||
RUN wget -nv -O bazel-installer.sh \
|
||||
https://github.com/bazelbuild/bazel/releases/download/$BAZEL_VERSION/\
|
||||
|
|
@ -53,6 +68,32 @@ bazel-$BAZEL_VERSION-installer-linux-x86_64.sh && \
|
|||
./bazel-installer.sh && \
|
||||
rm ./bazel-installer.sh
|
||||
|
||||
RUN bazel build javascript/net/grpc/web/... && \
|
||||
WORKDIR /github/grpc-web
|
||||
|
||||
# Copy only files necessary to build the protoc-gen-grpc-web first as an optimization because they
|
||||
# are rarely updated compared with the javascript files.
|
||||
COPY ./WORKSPACE ./WORKSPACE
|
||||
COPY ./bazel ./bazel
|
||||
COPY ./javascript/net/grpc/web/grpc_generator.cc javascript/net/grpc/web/grpc_generator.cc
|
||||
COPY ./javascript/net/grpc/web/BUILD.bazel javascript/net/grpc/web/BUILD.bazel
|
||||
|
||||
RUN bazel build javascript/net/grpc/web:protoc-gen-grpc-web && \
|
||||
cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/protoc-gen-grpc-web \
|
||||
/usr/local/bin/protoc-gen-grpc-web
|
||||
|
||||
COPY ./javascript ./javascript
|
||||
COPY ./packages ./packages
|
||||
|
||||
RUN cd ./packages/grpc-web && \
|
||||
npm install && \
|
||||
npm run build && \
|
||||
npm link
|
||||
|
||||
COPY ./Makefile ./Makefile
|
||||
COPY ./net ./net
|
||||
COPY ./scripts ./scripts
|
||||
COPY ./src ./src
|
||||
COPY ./test ./test
|
||||
|
||||
RUN /usr/local/bin/buildifier \
|
||||
--mode=check --lint=warn --warnings=all -r ./WORKSPACE bazel javascript net
|
||||
|
|
|
|||
|
|
@ -28,24 +28,16 @@ all: client
|
|||
client: proto-js compiled-js
|
||||
|
||||
compiled-js:
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/binary/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/commonjs/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/compatibility_tests/v3.0.0/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/compatibility_tests/v3.0.0/binary/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/compatibility_tests/v3.0.0/commonjs/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/compatibility_tests/v3.1.0/*_test.js || true
|
||||
rm $(ROOT_DIR)/$(PROTOBUF_PATH)/js/compatibility_tests/v3.1.0/binary/*_test.js || true
|
||||
./node_modules/.bin/google-closure-compiler \
|
||||
--js=*.js \
|
||||
--js=$(OUT_DIR)/*.js \
|
||||
--js=$(ROOT_DIR)/javascript \
|
||||
--js=$(ROOT_DIR)/$(PROTOBUF_PATH)/js \
|
||||
--js='!$(ROOT_DIR)/$(PROTOBUF_PATH)/js/**/*_test.js' \
|
||||
--js=$(NPM_DIR)/google-closure-library \
|
||||
--entry_point=goog:proto.grpc.gateway.testing.EchoServiceClient \
|
||||
--dependency_mode=PRUNE \
|
||||
--js_output_file compiled.js
|
||||
cd $(ROOT_DIR)/$(PROTOBUF_PATH) && git checkout .
|
||||
|
||||
proto-js:
|
||||
mkdir -p $(OUT_DIR)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
/index.js
|
||||
/package-lock.json
|
||||
/node_modules
|
||||
/generated/
|
||||
|
||||
package-lock.json
|
||||
node_modules/
|
||||
__pycache__/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
FROM selenium/standalone-chrome:93.0
|
||||
|
||||
# Matching the node version used in the node:12.22.6-stretch image.
|
||||
ARG NODE_VERSION=12.22.6
|
||||
|
||||
USER root
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y nodejs npm
|
||||
|
||||
# Install nvm (See https://github.com/creationix/nvm#install-script) and nodejs version per
|
||||
# specified in NODE_VERSION
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
|
||||
ENV NVM_DIR=$HOME/.nvm
|
||||
RUN . $NVM_DIR/nvm.sh \
|
||||
&& nvm install $NODE_VERSION \
|
||||
&& nvm alias default $NODE_VERSION \
|
||||
&& nvm use default
|
||||
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
|
||||
|
||||
WORKDIR /grpc-web
|
||||
|
||||
COPY ./packages ./packages
|
||||
RUN cd ./packages/grpc-web && \
|
||||
npm install
|
||||
|
||||
COPY ./javascript ./javascript
|
||||
COPY ./scripts ./scripts
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
const connect = require('gulp-connect');
|
||||
const gulp = require('gulp');
|
||||
|
||||
gulp.task('serve', () => {
|
||||
connect.server({
|
||||
// Serves the root of github repo so tests an access javascript files.
|
||||
root: '../../',
|
||||
port: 4000
|
||||
});
|
||||
});
|
||||
|
|
@ -19,19 +19,24 @@
|
|||
"build": "node scripts/build.js",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "mocha --timeout 60000 \"./test/**/*_test.js\""
|
||||
"test": "npm run test-jsunit && npm run test-mocha",
|
||||
"test-mocha": "mocha --timeout 10000 \"./test/**/*_test.js\"",
|
||||
"test-jsunit": "./scripts/generate_test_files.sh && ./scripts/run_jsunit_tests.sh && rm -rf ./generated"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/google-protobuf": "~3.7.0",
|
||||
"command-exists": "~1.2.8",
|
||||
"google-closure-compiler": "~20200224.0.0",
|
||||
"google-closure-deps": "~20210601.0.0",
|
||||
"google-closure-library": "~20201102.0.1",
|
||||
"google-protobuf": "~3.14.0",
|
||||
"gulp": "~4.0.2",
|
||||
"gulp-connect": "~5.7.0",
|
||||
"gulp-eval": "~1.0.0",
|
||||
"mocha": "~5.2.0",
|
||||
"mock-xmlhttprequest": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"typescript": "~3.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stores the configuration of Protractor. It is loaded by protractor to run
|
||||
* tests.
|
||||
*
|
||||
* Intended to be used through ./run_jsunit_test.sh
|
||||
*/
|
||||
|
||||
// Common configuration.
|
||||
config = {
|
||||
// Using jasmine to wrap Closure JSUnit tests.
|
||||
framework: 'jasmine',
|
||||
// The jasmine specs to run.
|
||||
specs: ['protractor_spec.js'],
|
||||
// Jasmine options. Increase the timeout to 5min instead of the default 30s.
|
||||
jasmineNodeOpts: {
|
||||
// Default time to wait in ms before a test fails.
|
||||
defaultTimeoutInterval: 5 * 60 * 1000 // 5 minutes
|
||||
}
|
||||
};
|
||||
|
||||
// Configuration for headless chrome.
|
||||
config.directConnect = true;
|
||||
config.multiCapabilities = [{
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
args: [ "--headless", "--disable-gpu", "--window-size=800,600",
|
||||
"--no-sandbox", "--disable-dev-shm-usage" ]
|
||||
}
|
||||
}];
|
||||
|
||||
exports.config = config;
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var allTests = require('./generated/all_tests');
|
||||
|
||||
var TEST_SERVER = 'http://localhost:4000';
|
||||
|
||||
describe('Run all Closure unit tests', function() {
|
||||
/**
|
||||
* Waits for current tests to be executed.
|
||||
* @param {function(!Object)} done The function called when the test is finished.
|
||||
* @param {function(!Error)} fail The function called when an unrecoverable error
|
||||
* happened during the test.
|
||||
*/
|
||||
var waitForTest = function(done, fail) {
|
||||
// executeScript runs the passed method in the "window" context of
|
||||
// the current test. JSUnit exposes hooks into the test's status through
|
||||
// the "G_testRunner" global object.
|
||||
browser.executeScript(function() {
|
||||
if (window['G_testRunner'] && window['G_testRunner']['isFinished']()) {
|
||||
return {
|
||||
isFinished: true,
|
||||
isSuccess: window['G_testRunner']['isSuccess'](),
|
||||
report: window['G_testRunner']['getReport']()
|
||||
};
|
||||
} else {
|
||||
return {'isFinished': false};
|
||||
}
|
||||
}).then(function(status) {
|
||||
if (status && status.isFinished) {
|
||||
done(status);
|
||||
} else {
|
||||
waitForTest(done, fail);
|
||||
}
|
||||
}, function(err) {
|
||||
// This can happen if the webdriver had an issue executing the script.
|
||||
fail(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the test cases for the file at the given testPath.
|
||||
* @param {!string} testPath The path of the current test suite to execute.
|
||||
*/
|
||||
var executeTest = function(testPath) {
|
||||
it('runs ' + testPath + ' with success', function(done) {
|
||||
/**
|
||||
* Runs the test routines for a given test path.
|
||||
* @param {function()} done The function to run on completion.
|
||||
*/
|
||||
var runRoutine = function(done) {
|
||||
browser.navigate()
|
||||
.to(TEST_SERVER + '/' + testPath)
|
||||
.then(function() {
|
||||
waitForTest(function(status) {
|
||||
expect(status).toBeSuccess();
|
||||
done();
|
||||
}, function(err) {
|
||||
done.fail(err);
|
||||
});
|
||||
}, function(err) {
|
||||
done.fail(err);
|
||||
});
|
||||
};
|
||||
// Run the test routine.
|
||||
runRoutine(done);
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
// This custom matcher allows for cleaner reports.
|
||||
toBeSuccess: function() {
|
||||
return {
|
||||
// Checks that the status report is successful, otherwise displays
|
||||
// the report as error message.
|
||||
compare: function(status) {
|
||||
return {
|
||||
pass: status.isSuccess,
|
||||
message: 'Some test cases failed!\n\n' + status.report
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Run all tests.
|
||||
for (var i = 0; i < allTests.length; i++) {
|
||||
var testPath = allTests[i];
|
||||
executeTest(testPath);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2021 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Common methods and constants for generating test files."""
|
||||
|
||||
import os
|
||||
from typing import Iterator
|
||||
|
||||
# The directory in which test HTML files are generated.
|
||||
GENERATED_TEST_BASE_PATH = "generated/test_htmls/"
|
||||
|
||||
|
||||
def read_file(path: str) -> str:
|
||||
"""Reads the content of a file."""
|
||||
with open(path) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def write_file(path: str, content: str):
|
||||
"""Writes a string to file, overwriting existing content; intermediate
|
||||
directories are created if not present."""
|
||||
dir_name = os.path.dirname(path)
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
def get_files_with_suffix(root_dir: str, suffix: str) -> Iterator[str]:
|
||||
"""Yields file names under a directory with a given suffix."""
|
||||
for dir_path, _, file_names in os.walk(root_dir):
|
||||
for file_name in file_names:
|
||||
if file_name.endswith(suffix):
|
||||
yield os.path.join(dir_path, file_name)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2021 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Generates the all_tests.js file for consumption by Protractor.
|
||||
|
||||
Usage:
|
||||
$ cd packages/grpc-web
|
||||
$ python3 ./scripts/gen_test_htmls.py # Prerequisite
|
||||
$ python3 ./scripts/gen_all_tests_js.py
|
||||
"""
|
||||
|
||||
from string import Template
|
||||
|
||||
import common
|
||||
|
||||
ALL_TESTS_TEMPLATE_FILE = './scripts/template_all_tests_js.txt'
|
||||
|
||||
# The path of the generated all_tests.js file
|
||||
GENERATED_ALL_TESTS_JS_PATH = './generated/all_tests.js'
|
||||
|
||||
# File paths needs to be prepended by the relative path of the grpc-web package
|
||||
# because web server is hosting the root of github repo for tests to access the
|
||||
# javascript files.
|
||||
GRPC_WEB_BASE_DIR = 'packages/grpc-web'
|
||||
|
||||
|
||||
def main():
|
||||
template_data = common.read_file(ALL_TESTS_TEMPLATE_FILE)
|
||||
template = Template(template_data)
|
||||
|
||||
test_html_paths = []
|
||||
for file_name in common.get_files_with_suffix(
|
||||
common.GENERATED_TEST_BASE_PATH, '_test.html'):
|
||||
test_html_paths.append(" '%s/%s'," % (GRPC_WEB_BASE_DIR, file_name))
|
||||
# Example output paths:
|
||||
# 'packages/grpc-web/generated/test_htmls/javascript__net__grpc__web__grpcwebclientbase_test.html',
|
||||
# 'packages/grpc-web/generated/test_htmls/javascript__net__grpc__web__grpcwebstreamparser_test.html',
|
||||
test_html_paths_str = "\n".join(test_html_paths)
|
||||
|
||||
# Writes the generated output to the all_tests.js file.
|
||||
common.write_file(GENERATED_ALL_TESTS_JS_PATH,
|
||||
template.substitute(test_html_paths=test_html_paths_str))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
# Copyright 2021 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Generates *_test.html files from *_test.js files.
|
||||
|
||||
Usage:
|
||||
$ cd packages/grpc-web
|
||||
$ python3 ./scripts/gen_test_htmls.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from string import Template
|
||||
|
||||
import common
|
||||
|
||||
# The directories containing JS tests.
|
||||
DIRECTORIES_WITH_TESTS = ["../../javascript"]
|
||||
|
||||
TEST_HTML_TEMPLATE_FILE = './scripts/template_test_html.txt'
|
||||
|
||||
|
||||
def main():
|
||||
template_data = common.read_file(TEST_HTML_TEMPLATE_FILE)
|
||||
template = Template(template_data)
|
||||
for directory in DIRECTORIES_WITH_TESTS:
|
||||
for js_file_path in common.get_files_with_suffix(
|
||||
directory, "_test.js"):
|
||||
_gen_test_html(js_file_path, template)
|
||||
|
||||
|
||||
def _gen_test_html(js_file_path: str, template: Template):
|
||||
"""Generates a Closure test wrapper HTML and saves it to the filesystem."""
|
||||
# Generates the test_file_name so that:
|
||||
# ../../javascript/net/grpc/web/grpcwebclientbase_test.js
|
||||
# will now be named:
|
||||
# javascript__net__grpc__web__grpcwebclientbase_test.html
|
||||
test_file_name = js_file_path
|
||||
while test_file_name.startswith('../'):
|
||||
test_file_name = test_file_name[3:]
|
||||
test_file_name = test_file_name.replace('/', '__')
|
||||
test_file_name = os.path.splitext(test_file_name)[0] + '.html'
|
||||
|
||||
# Generates the test HTML using the package name of the test file
|
||||
package_name = _extract_closure_package(js_file_path)
|
||||
generated_html = template.substitute(package=package_name)
|
||||
|
||||
# Writes the test HTML files
|
||||
common.write_file(common.GENERATED_TEST_BASE_PATH + test_file_name,
|
||||
generated_html)
|
||||
|
||||
|
||||
def _extract_closure_package(js_file_path) -> str:
|
||||
"""Extracts the package name from goog.provide() or goog.module() in the
|
||||
JS file."""
|
||||
js_data = common.read_file(js_file_path)
|
||||
matches = re.search(r"goog\.(provide|module)\([\n\s]*'(.+)'\);", js_data)
|
||||
|
||||
if matches is None:
|
||||
raise ValueError("goog.provide() or goog.module() not found in file")
|
||||
|
||||
return matches.group(2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2021 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Generates the temporary files needed for tests to run, putting them in the
|
||||
# generated/ directory.
|
||||
#
|
||||
# Usage:
|
||||
# $ cd packages/grpc-web
|
||||
# $ ./scripts/generate_test_files.sh
|
||||
|
||||
set -ex
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
REPO_DIR=$(realpath "${SCRIPT_DIR}/../")
|
||||
JAVASCRIPT_DIR=$(realpath "${SCRIPT_DIR}/../../../javascript")
|
||||
GEN_DIR="$REPO_DIR/generated"
|
||||
|
||||
cd "$REPO_DIR"
|
||||
|
||||
mkdir -p "$GEN_DIR"
|
||||
|
||||
echo "Generating dependency file..."
|
||||
$(npm bin)/closure-make-deps \
|
||||
--closure-path="node_modules/google-closure-library/closure/goog" \
|
||||
--file="node_modules/google-closure-library/closure/goog/deps.js" \
|
||||
--root="$JAVASCRIPT_DIR" \
|
||||
--exclude="$GEN_DIR/all_tests.js" \
|
||||
--exclude="$GEN_DIR/deps.js" \
|
||||
> "$GEN_DIR/deps.js"
|
||||
|
||||
echo "Generating test HTML files..."
|
||||
python3 ./scripts/gen_test_htmls.py
|
||||
python3 ./scripts/gen_all_tests_js.py
|
||||
|
||||
echo "Done."
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2021 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This script starts a local HTTP server to serve test files, starts a Selenium Webdriver, and
|
||||
# runs the unit tests using Protractor.
|
||||
# Run locally with Pratractor:
|
||||
#
|
||||
# Usage (under ./packages/grpc-web):
|
||||
# $ ./scripts/generate_test_files.sh # Required first step
|
||||
# $ ./scripts/run_jsunit_tests.sh
|
||||
#
|
||||
# Or (preferred use):
|
||||
# $ npm run test-jsunit
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname $(dirname "$0"))"
|
||||
NPM_BIN_PATH=$(npm bin)
|
||||
PROTRACTOR_BIN_PATH="./node_modules/protractor/bin"
|
||||
|
||||
function cleanup () {
|
||||
echo "Killing HTTP Server..."
|
||||
kill $serverPid
|
||||
}
|
||||
|
||||
# Start the local webserver.
|
||||
echo "Starting local HTP Server..."
|
||||
$NPM_BIN_PATH/gulp serve &
|
||||
serverPid=$!
|
||||
echo "Local HTTP Server started with PID $serverPid."
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "Using Headless Chrome."
|
||||
# Updates Selenium Webdriver.
|
||||
echo "$PROTRACTOR_BIN_PATH/webdriver-manager update --gecko=false"
|
||||
$PROTRACTOR_BIN_PATH/webdriver-manager update --gecko=false
|
||||
|
||||
# Run the tests using Protractor! (Protractor should run selenium automatically)
|
||||
$PROTRACTOR_BIN_PATH/protractor protractor.conf.js
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
var allTests = [
|
||||
$test_html_paths
|
||||
];
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = allTests;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!-- Copyright (c) 2021 Google Inc. All rights reserved. -->
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta charset="UTF-8" />
|
||||
<script src="../../node_modules/google-closure-library/closure/goog/base.js"></script>
|
||||
<script src="../deps.js"></script>
|
||||
<script>
|
||||
goog.require('$package');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -83,6 +83,9 @@ describe('grpc-web generated code eval test (typescript)', function() {
|
|||
|
||||
it('should eval', function() {
|
||||
execSync(genCodeCmd);
|
||||
execSync(`tsc --strict ${genTsCodePath}`);
|
||||
// --skipLibCheck is needed because some of our node_modules/ targets es6
|
||||
// but our test doesn't pass with `--target es6`
|
||||
// TODO: Find out how we can enable --target es6!
|
||||
execSync(`tsc --strict --skipLibCheck ${genTsCodePath}`);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,11 @@ function runTscCmd(tscCmd) {
|
|||
}
|
||||
}
|
||||
const outputDir = './test/tsc-tests/generated';
|
||||
const tscCompilerOptions = `--allowJs --strict --noImplicitReturns`
|
||||
// --skipLibCheck is needed because some of our node_modules/ targets es6 but
|
||||
// our test doesn't pass with `--target es6`
|
||||
// TODO: Find out how we can enable --target es6!
|
||||
const tscCompilerOptions =
|
||||
`--allowJs --strict --noImplicitReturns --skipLibCheck`;
|
||||
|
||||
describe('tsc test01: nested messages', function() {
|
||||
before(function() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2018 Google LLC
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,12 +17,9 @@ set -ex
|
|||
# This script is intended to be run within the base image from
|
||||
# net/grpc/gateway/docker/prereqs/Dockerfile
|
||||
|
||||
# Ensures all Bazel targets builds
|
||||
cd /github/grpc-web && \
|
||||
bazel clean && \
|
||||
bazel test --cache_test_results=no \
|
||||
bazel build \
|
||||
//javascript/net/grpc/web/... \
|
||||
//net/grpc/gateway/examples/...
|
||||
|
||||
cd /github/grpc-web/packages/grpc-web && \
|
||||
npm run prepare && \
|
||||
npm test
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
set -ex
|
||||
|
||||
# This script is intended to be run within the base image from
|
||||
# packages/grpc-web/docker/jsunit-test/Dockerfile
|
||||
|
||||
cd /grpc-web/packages/grpc-web
|
||||
|
||||
npm run test-jsunit
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
set -ex
|
||||
|
||||
# This script is intended to be run within the base image from
|
||||
# net/grpc/gateway/docker/prereqs/Dockerfile
|
||||
|
||||
cd /github/grpc-web/packages/grpc-web
|
||||
npm run prepare && \
|
||||
npm run test-mocha
|
||||
|
|
@ -22,25 +22,34 @@ cd "${REPO_DIR}"
|
|||
|
||||
|
||||
# These programs need to be already installed
|
||||
progs=(docker docker-compose npm curl)
|
||||
progs=(docker docker-compose curl)
|
||||
for p in "${progs[@]}"
|
||||
do
|
||||
command -v "$p" > /dev/null 2>&1 || \
|
||||
{ echo >&2 "$p is required but not installed. Aborting."; exit 1; }
|
||||
done
|
||||
|
||||
##########################################################
|
||||
# Step 1: Run all unit tests
|
||||
##########################################################
|
||||
echo -e "\n[Running] Basic test #1 - Runnning unit tests"
|
||||
# Run jsunit tests
|
||||
docker-compose build jsunit-test
|
||||
docker run --rm grpcweb/jsunit-test /bin/bash \
|
||||
/grpc-web/scripts/docker-run-jsunit-tests.sh
|
||||
|
||||
# Build all relevant docker images. They should all build successfully.
|
||||
if [[ "$MASTER" == "1" ]]; then
|
||||
# Build all for continuous_integration
|
||||
docker-compose build
|
||||
else
|
||||
# Only build a subset of docker images for presubmit runs
|
||||
docker-compose build common prereqs envoy node-server \
|
||||
commonjs-client ts-client
|
||||
fi
|
||||
# Run (mocha) unit tests
|
||||
docker-compose build prereqs
|
||||
docker run --rm grpcweb/prereqs /bin/bash \
|
||||
/github/grpc-web/scripts/docker-run-mocha-tests.sh
|
||||
|
||||
|
||||
##########################################################
|
||||
# Step 2: Test echo server
|
||||
##########################################################
|
||||
echo -e "\n[Running] Basic test #2 - Testing echo server"
|
||||
docker-compose build prereqs envoy node-server
|
||||
|
||||
# Bring up the Echo server and the Envoy proxy (in background).
|
||||
# The 'sleep' seems necessary for the docker containers to be fully up
|
||||
# and listening before we test the with curl requests
|
||||
|
|
@ -53,11 +62,22 @@ source ./scripts/test-proxy.sh
|
|||
docker-compose down
|
||||
|
||||
|
||||
# Run unit tests from npm package
|
||||
docker run --rm grpcweb/prereqs /bin/bash \
|
||||
/github/grpc-web/scripts/docker-run-tests.sh
|
||||
##########################################################
|
||||
# Step 3: Test all Dockerfile and Bazel targets can build!
|
||||
##########################################################
|
||||
echo -e "\n[Running] Basic test #3 - Testing everything buids"
|
||||
if [[ "$MASTER" == "1" ]]; then
|
||||
# Build all for continuous_integration
|
||||
docker-compose build
|
||||
else
|
||||
# Only build a subset of docker images for presubmit runs
|
||||
docker-compose build commonjs-client closure-client ts-client
|
||||
fi
|
||||
|
||||
# Run build tests to ensure all Bazel targets can build.
|
||||
docker run --rm grpcweb/prereqs /bin/bash \
|
||||
/github/grpc-web/scripts/docker-run-build-tests.sh
|
||||
|
||||
# Clean up
|
||||
git clean -f -d -x
|
||||
echo 'Completed'
|
||||
echo 'Basic tests completed successfully!'
|
||||
|
|
|
|||
|
|
@ -34,12 +34,13 @@ do
|
|||
{ echo >&2 "$p is required but not installed. Aborting."; exit 1; }
|
||||
done
|
||||
|
||||
|
||||
# Build all relevant docker images. They should all build successfully.
|
||||
docker-compose build common prereqs node-interop-server interop-client java-interop-server
|
||||
docker-compose build prereqs node-interop-server java-interop-server
|
||||
|
||||
|
||||
# Run interop tests
|
||||
##########################################################
|
||||
# Run interop tests (against Envoy)
|
||||
##########################################################
|
||||
echo -e "\n[Running] Interop test #1 - against Envoy"
|
||||
pid1=$(docker run -d \
|
||||
-v "$(pwd)"/test/interop/envoy.yaml:/etc/envoy/envoy.yaml:ro \
|
||||
--network=host envoyproxy/envoy:v1.17.0)
|
||||
|
|
@ -51,9 +52,10 @@ docker rm -f "$pid1"
|
|||
docker rm -f "$pid2"
|
||||
|
||||
|
||||
#
|
||||
# Run interop tests against grpc-web java connector code
|
||||
#
|
||||
##########################################################
|
||||
# Run interop tests (against grpc-web Java connector code)
|
||||
##########################################################
|
||||
echo -e "\n[Running] Interop test #2 - against Java interop server"
|
||||
pid3=$(docker run -d --network=host grpcweb/java-interop-server)
|
||||
run_tests
|
||||
docker rm -f "$pid3"
|
||||
|
|
|
|||
|
|
@ -47,4 +47,6 @@ echo "$s1" | base64 -d | xxd
|
|||
# Take the 28 bytes we cut out above, the base64-encoded string should be this
|
||||
if [[ "$s1" != "AAAAAAcKBWhlbGxvgGdycGMtc3RhdHVzOjANCg==" ]]; then
|
||||
exit 1;
|
||||
else
|
||||
echo "Envoy proxy test successful!"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const grpc = {};
|
|||
grpc.web = require('grpc-web');
|
||||
|
||||
const SERVER_HOST = 'http://localhost:8080';
|
||||
const TIMEOUT_MS = 1000; // 1 second
|
||||
|
||||
function multiDone(done, count) {
|
||||
return function() {
|
||||
|
|
@ -189,11 +190,12 @@ if (typeof window === 'undefined') { // Running from Node
|
|||
} else {
|
||||
console.log('Testing grpc-web-text mode...');
|
||||
}
|
||||
|
||||
|
||||
describe('grpc-web interop tests', function() {
|
||||
Object.keys(testCases).forEach((testCase) => {
|
||||
if (argv.mode == 'binary' && testCases[testCase].skipBinaryMode) return;
|
||||
it('should pass '+testCase, testCases[testCase].testFunc);
|
||||
it('should pass '+testCase, testCases[testCase].testFunc)
|
||||
.timeout(TIMEOUT_MS);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
|
@ -216,6 +218,6 @@ if (typeof window === 'undefined') { // Running from Node
|
|||
if (!doneCalled) {
|
||||
throw testCase+': failed. Not all done() are called';
|
||||
}
|
||||
}, 500);
|
||||
}, TIMEOUT_MS);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"description": "gRPC-Web Interop Test Client",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"test": "mocha -b --timeout 500 interop_client.js"
|
||||
"test": "mocha -b interop_client.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"google-protobuf": "~3.14.0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue