mirror of https://github.com/grpc/grpc-dart.git
Fix: Migrate off legacy JS/HTML APIs (#750)
* update: Migrate off legacy JS/HTML apis * update: use dart.library.js_interop in place of dart.library.html * update: dart format xhr_transport.dart and update dart sdk to v3.4.0 in workflows * update: use if instead of switch case in xhr_transport.dart * update: upgrade web package to v1.1.0 * refactor: use Uint8List for sending data over XHR rather than Int8List * refactor: eta-reduction of call to request.setRequestHeader * Update client_xhr_transport_test to avoid dart:html, updating xhr_transport to support testability * fixup tests --------- Co-authored-by: minoic <minoic2020@gmail.com> Co-authored-by: Moritz <mosum@google.com>
This commit is contained in:
parent
7f9042f79e
commit
840661415d
|
|
@ -6,6 +6,7 @@
|
||||||
the connection, as defined in the gRPC spec.
|
the connection, as defined in the gRPC spec.
|
||||||
* Upgrade to `package:lints` version 5.0.0 and Dart SDK version 3.5.0.
|
* Upgrade to `package:lints` version 5.0.0 and Dart SDK version 3.5.0.
|
||||||
* Upgrade `example/grpc-web` code.
|
* Upgrade `example/grpc-web` code.
|
||||||
|
* Update xhr transport to migrate off legacy JS/HTML apis.
|
||||||
|
|
||||||
## 4.0.1
|
## 4.0.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import 'src/client/grpc_or_grpcweb_channel_grpc.dart'
|
import 'src/client/grpc_or_grpcweb_channel_grpc.dart'
|
||||||
if (dart.library.html) 'src/client/grpc_or_grpcweb_channel_web.dart';
|
if (dart.library.js_interop) 'src/client/grpc_or_grpcweb_channel_web.dart';
|
||||||
import 'src/client/http2_channel.dart';
|
import 'src/client/http2_channel.dart';
|
||||||
import 'src/client/options.dart';
|
import 'src/client/options.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
// ignore: deprecated_member_use (#756)
|
import 'dart:js_interop';
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:web/web.dart';
|
||||||
|
|
||||||
import '../../client/call.dart';
|
import '../../client/call.dart';
|
||||||
import '../../shared/message.dart';
|
import '../../shared/message.dart';
|
||||||
|
|
@ -31,7 +31,7 @@ import 'web_streams.dart';
|
||||||
const _contentTypeKey = 'Content-Type';
|
const _contentTypeKey = 'Content-Type';
|
||||||
|
|
||||||
class XhrTransportStream implements GrpcTransportStream {
|
class XhrTransportStream implements GrpcTransportStream {
|
||||||
final HttpRequest _request;
|
final IXMLHttpRequest _request;
|
||||||
final ErrorHandler _onError;
|
final ErrorHandler _onError;
|
||||||
final Function(XhrTransportStream stream) _onDone;
|
final Function(XhrTransportStream stream) _onDone;
|
||||||
bool _headersReceived = false;
|
bool _headersReceived = false;
|
||||||
|
|
@ -50,19 +50,20 @@ class XhrTransportStream implements GrpcTransportStream {
|
||||||
{required ErrorHandler onError, required onDone})
|
{required ErrorHandler onError, required onDone})
|
||||||
: _onError = onError,
|
: _onError = onError,
|
||||||
_onDone = onDone {
|
_onDone = onDone {
|
||||||
_outgoingMessages.stream
|
_outgoingMessages.stream.map(frame).listen(
|
||||||
.map(frame)
|
(data) => _request.send(Uint8List.fromList(data).toJS),
|
||||||
.listen((data) => _request.send(data), cancelOnError: true);
|
cancelOnError: true,
|
||||||
|
onError: _onError);
|
||||||
|
|
||||||
_request.onReadyStateChange.listen((data) {
|
_request.onReadyStateChange.listen((_) {
|
||||||
if (_incomingProcessor.isClosed) {
|
if (_incomingProcessor.isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (_request.readyState) {
|
switch (_request.readyState) {
|
||||||
case HttpRequest.HEADERS_RECEIVED:
|
case XMLHttpRequest.HEADERS_RECEIVED:
|
||||||
_onHeadersReceived();
|
_onHeadersReceived();
|
||||||
break;
|
break;
|
||||||
case HttpRequest.DONE:
|
case XMLHttpRequest.DONE:
|
||||||
_onRequestDone();
|
_onRequestDone();
|
||||||
_close();
|
_close();
|
||||||
break;
|
break;
|
||||||
|
|
@ -82,13 +83,11 @@ class XhrTransportStream implements GrpcTransportStream {
|
||||||
if (_incomingProcessor.isClosed) {
|
if (_incomingProcessor.isClosed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Use response over responseText as most browsers don't support
|
final responseText = _request.responseText;
|
||||||
// using responseText during an onProgress event.
|
|
||||||
final responseString = _request.response as String;
|
|
||||||
final bytes = Uint8List.fromList(
|
final bytes = Uint8List.fromList(
|
||||||
responseString.substring(_requestBytesRead).codeUnits)
|
responseText.substring(_requestBytesRead).codeUnits)
|
||||||
.buffer;
|
.buffer;
|
||||||
_requestBytesRead = responseString.length;
|
_requestBytesRead = responseText.length;
|
||||||
_incomingProcessor.add(bytes);
|
_incomingProcessor.add(bytes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -123,9 +122,11 @@ class XhrTransportStream implements GrpcTransportStream {
|
||||||
if (!_headersReceived && !_validateResponseState()) {
|
if (!_headersReceived && !_validateResponseState()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_request.response == null) {
|
if (_request.status != 200) {
|
||||||
_onError(
|
_onError(
|
||||||
GrpcError.unavailable('XhrConnection request null response', null,
|
GrpcError.unavailable(
|
||||||
|
'Request failed with status: ${_request.status}',
|
||||||
|
null,
|
||||||
_request.responseText),
|
_request.responseText),
|
||||||
StackTrace.current);
|
StackTrace.current);
|
||||||
return;
|
return;
|
||||||
|
|
@ -145,6 +146,110 @@ class XhrTransportStream implements GrpcTransportStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XMLHttpRequest is an extension type and can't be extended or implemented.
|
||||||
|
// This interface is used to allow for mocking XMLHttpRequest in tests of
|
||||||
|
// XhrClientConnection.
|
||||||
|
@visibleForTesting
|
||||||
|
abstract interface class IXMLHttpRequest {
|
||||||
|
Stream<Event> get onReadyStateChange;
|
||||||
|
Stream<ProgressEvent> get onProgress;
|
||||||
|
Stream<ProgressEvent> get onError;
|
||||||
|
int get readyState;
|
||||||
|
JSAny? get response;
|
||||||
|
String get responseText;
|
||||||
|
Map<String, String> get responseHeaders;
|
||||||
|
int get status;
|
||||||
|
|
||||||
|
set responseType(String responseType);
|
||||||
|
set withCredentials(bool withCredentials);
|
||||||
|
|
||||||
|
void abort();
|
||||||
|
void open(
|
||||||
|
String method,
|
||||||
|
String url, [
|
||||||
|
// external default is true
|
||||||
|
bool async = true,
|
||||||
|
String? username,
|
||||||
|
String? password,
|
||||||
|
]);
|
||||||
|
void overrideMimeType(String mimeType);
|
||||||
|
void send([JSAny? body]);
|
||||||
|
void setRequestHeader(String header, String value);
|
||||||
|
|
||||||
|
// This method should only be used in production code.
|
||||||
|
XMLHttpRequest toXMLHttpRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// IXMLHttpRequest that delegates to a real XMLHttpRequest.
|
||||||
|
class XMLHttpRequestImpl implements IXMLHttpRequest {
|
||||||
|
final XMLHttpRequest _xhr = XMLHttpRequest();
|
||||||
|
|
||||||
|
XMLHttpRequestImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Event> get onReadyStateChange => _xhr.onReadyStateChange;
|
||||||
|
@override
|
||||||
|
Stream<ProgressEvent> get onProgress => _xhr.onProgress;
|
||||||
|
@override
|
||||||
|
Stream<ProgressEvent> get onError => _xhr.onError;
|
||||||
|
@override
|
||||||
|
int get readyState => _xhr.readyState;
|
||||||
|
@override
|
||||||
|
Map<String, String> get responseHeaders => _xhr.responseHeaders;
|
||||||
|
@override
|
||||||
|
JSAny? get response => _xhr.response;
|
||||||
|
@override
|
||||||
|
String get responseText => _xhr.responseText;
|
||||||
|
@override
|
||||||
|
int get status => _xhr.status;
|
||||||
|
|
||||||
|
@override
|
||||||
|
set responseType(String responseType) {
|
||||||
|
_xhr.responseType = responseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
set withCredentials(bool withCredentials) {
|
||||||
|
_xhr.withCredentials = withCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void abort() {
|
||||||
|
_xhr.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void open(
|
||||||
|
String method,
|
||||||
|
String url, [
|
||||||
|
bool async = true,
|
||||||
|
String? username,
|
||||||
|
String? password,
|
||||||
|
]) {
|
||||||
|
_xhr.open(method, url, async, username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void overrideMimeType(String mimeType) {
|
||||||
|
_xhr.overrideMimeType(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setRequestHeader(String header, String value) {
|
||||||
|
_xhr.setRequestHeader(header, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void send([JSAny? body]) {
|
||||||
|
_xhr.send(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
XMLHttpRequest toXMLHttpRequest() {
|
||||||
|
return _xhr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class XhrClientConnection implements ClientConnection {
|
class XhrClientConnection implements ClientConnection {
|
||||||
final Uri uri;
|
final Uri uri;
|
||||||
|
|
||||||
|
|
@ -154,20 +259,20 @@ class XhrClientConnection implements ClientConnection {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get authority => uri.authority;
|
String get authority => uri.authority;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get scheme => uri.scheme;
|
String get scheme => uri.scheme;
|
||||||
|
|
||||||
void _initializeRequest(HttpRequest request, Map<String, String> metadata) {
|
void _initializeRequest(
|
||||||
for (final header in metadata.keys) {
|
IXMLHttpRequest request, Map<String, String> metadata) {
|
||||||
request.setRequestHeader(header, metadata[header]!);
|
metadata.forEach(request.setRequestHeader);
|
||||||
}
|
|
||||||
// Overriding the mimetype allows us to stream and parse the data
|
// Overriding the mimetype allows us to stream and parse the data
|
||||||
request.overrideMimeType('text/plain; charset=x-user-defined');
|
request.overrideMimeType('text/plain; charset=x-user-defined');
|
||||||
request.responseType = 'text';
|
request.responseType = 'text';
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
HttpRequest createHttpRequest() => HttpRequest();
|
IXMLHttpRequest createHttpRequest() => XMLHttpRequestImpl();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GrpcTransportStream makeRequest(String path, Duration? timeout,
|
GrpcTransportStream makeRequest(String path, Duration? timeout,
|
||||||
|
|
@ -195,11 +300,16 @@ class XhrClientConnection implements ClientConnection {
|
||||||
_initializeRequest(request, metadata);
|
_initializeRequest(request, metadata);
|
||||||
|
|
||||||
final transportStream =
|
final transportStream =
|
||||||
XhrTransportStream(request, onError: onError, onDone: _removeStream);
|
_createXhrTransportStream(request, onError, _removeStream);
|
||||||
_requests.add(transportStream);
|
_requests.add(transportStream);
|
||||||
return transportStream;
|
return transportStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XhrTransportStream _createXhrTransportStream(IXMLHttpRequest request,
|
||||||
|
ErrorHandler onError, void Function(XhrTransportStream stream) onDone) {
|
||||||
|
return XhrTransportStream(request, onError: onError, onDone: onDone);
|
||||||
|
}
|
||||||
|
|
||||||
void _removeStream(XhrTransportStream stream) {
|
void _removeStream(XhrTransportStream stream) {
|
||||||
_requests.remove(stream);
|
_requests.remove(stream);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ dependencies:
|
||||||
http2: ^2.2.0
|
http2: ^2.2.0
|
||||||
protobuf: '>=2.0.0 <4.0.0'
|
protobuf: '>=2.0.0 <4.0.0'
|
||||||
clock: ^1.1.1
|
clock: ^1.1.1
|
||||||
|
web: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.0.0
|
build_runner: ^2.0.0
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
// ignore: deprecated_member_use (#756)
|
import 'dart:js_interop';
|
||||||
import 'dart:html';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:async/async.dart';
|
import 'package:async/async.dart';
|
||||||
import 'package:grpc/src/client/call.dart';
|
import 'package:grpc/src/client/call.dart';
|
||||||
import 'package:grpc/src/client/transport/xhr_transport.dart';
|
import 'package:grpc/src/client/transport/xhr_transport.dart';
|
||||||
|
|
@ -28,12 +27,13 @@ import 'package:grpc/src/shared/status.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:stream_transform/stream_transform.dart';
|
import 'package:stream_transform/stream_transform.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
import 'package:web/web.dart';
|
||||||
|
|
||||||
final readyStateChangeEvent =
|
final readyStateChangeEvent =
|
||||||
Event('readystatechange', canBubble: false, cancelable: false);
|
Event('readystatechange', EventInit(bubbles: false, cancelable: false));
|
||||||
final progressEvent = ProgressEvent('onloadstart');
|
final progressEvent = ProgressEvent('onloadstart');
|
||||||
|
|
||||||
class MockHttpRequest extends Mock implements HttpRequest {
|
class MockHttpRequest extends Mock implements IXMLHttpRequest {
|
||||||
MockHttpRequest({int? code}) : status = code ?? 200;
|
MockHttpRequest({int? code}) : status = code ?? 200;
|
||||||
// ignore: close_sinks
|
// ignore: close_sinks
|
||||||
StreamController<Event> readyStateChangeController =
|
StreamController<Event> readyStateChangeController =
|
||||||
|
|
@ -54,6 +54,10 @@ class MockHttpRequest extends Mock implements HttpRequest {
|
||||||
@override
|
@override
|
||||||
final int status;
|
final int status;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get responseText =>
|
||||||
|
super.noSuchMethod(Invocation.getter(#responseText), returnValue: '');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get readyState =>
|
int get readyState =>
|
||||||
super.noSuchMethod(Invocation.getter(#readyState), returnValue: -1);
|
super.noSuchMethod(Invocation.getter(#readyState), returnValue: -1);
|
||||||
|
|
@ -73,7 +77,7 @@ class MockXhrClientConnection extends XhrClientConnection {
|
||||||
final int _statusCode;
|
final int _statusCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
HttpRequest createHttpRequest() {
|
IXMLHttpRequest createHttpRequest() {
|
||||||
final request = MockHttpRequest(code: _statusCode);
|
final request = MockHttpRequest(code: _statusCode);
|
||||||
latestRequest = request;
|
latestRequest = request;
|
||||||
return request;
|
return request;
|
||||||
|
|
@ -210,8 +214,8 @@ void main() {
|
||||||
await stream.terminate();
|
await stream.terminate();
|
||||||
|
|
||||||
final expectedData = frame(data);
|
final expectedData = frame(data);
|
||||||
expect(verify(connection.latestRequest.send(captureAny)).captured.single,
|
verify(
|
||||||
expectedData);
|
connection.latestRequest.send(Uint8List.fromList(expectedData).toJS));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Stream handles headers properly', () async {
|
test('Stream handles headers properly', () async {
|
||||||
|
|
@ -227,14 +231,13 @@ void main() {
|
||||||
(error, _) => fail(error.toString()));
|
(error, _) => fail(error.toString()));
|
||||||
|
|
||||||
when(transport.latestRequest.responseHeaders).thenReturn(responseHeaders);
|
when(transport.latestRequest.responseHeaders).thenReturn(responseHeaders);
|
||||||
when(transport.latestRequest.response)
|
when(transport.latestRequest.responseText)
|
||||||
.thenReturn(String.fromCharCodes(frame(<int>[])));
|
.thenReturn(String.fromCharCodes(frame(<int>[])));
|
||||||
|
|
||||||
// Set expectation for request readyState and generate two readyStateChange
|
// Set expectation for request readyState and generate two readyStateChange
|
||||||
// events, so that incomingMessages stream completes.
|
// events, so that incomingMessages stream completes.
|
||||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
final readyStates = [XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE];
|
||||||
when(transport.latestRequest.readyState)
|
when(transport.latestRequest.readyState).thenReturnInOrder(readyStates);
|
||||||
.thenAnswer((_) => readyStates.removeAt(0));
|
|
||||||
transport.latestRequest.readyStateChangeController
|
transport.latestRequest.readyStateChangeController
|
||||||
.add(readyStateChangeEvent);
|
.add(readyStateChangeEvent);
|
||||||
transport.latestRequest.readyStateChangeController
|
transport.latestRequest.readyStateChangeController
|
||||||
|
|
@ -269,13 +272,12 @@ void main() {
|
||||||
final encodedString = String.fromCharCodes(encodedTrailers);
|
final encodedString = String.fromCharCodes(encodedTrailers);
|
||||||
|
|
||||||
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
|
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
|
||||||
when(connection.latestRequest.response).thenReturn(encodedString);
|
when(connection.latestRequest.responseText).thenReturn(encodedString);
|
||||||
|
|
||||||
// Set expectation for request readyState and generate events so that
|
// Set expectation for request readyState and generate events so that
|
||||||
// incomingMessages stream completes.
|
// incomingMessages stream completes.
|
||||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
when(connection.latestRequest.readyState).thenReturnInOrder(
|
||||||
when(connection.latestRequest.readyState)
|
[XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE]);
|
||||||
.thenAnswer((_) => readyStates.removeAt(0));
|
|
||||||
connection.latestRequest.readyStateChangeController
|
connection.latestRequest.readyStateChangeController
|
||||||
.add(readyStateChangeEvent);
|
.add(readyStateChangeEvent);
|
||||||
connection.latestRequest.progressController.add(progressEvent);
|
connection.latestRequest.progressController.add(progressEvent);
|
||||||
|
|
@ -305,13 +307,11 @@ void main() {
|
||||||
final encodedString = String.fromCharCodes(encoded);
|
final encodedString = String.fromCharCodes(encoded);
|
||||||
|
|
||||||
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
|
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
|
||||||
when(connection.latestRequest.response).thenReturn(encodedString);
|
when(connection.latestRequest.responseText).thenReturn(encodedString);
|
||||||
|
|
||||||
// Set expectation for request readyState and generate events so that
|
// Set expectation for request readyState and generate events so that
|
||||||
// incomingMessages stream completes.
|
// incomingMessages stream completes.
|
||||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
when(connection.latestRequest.readyState).thenReturnInOrder(
|
||||||
when(connection.latestRequest.readyState)
|
[XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE]);
|
||||||
.thenAnswer((_) => readyStates.removeAt(0));
|
|
||||||
connection.latestRequest.readyStateChangeController
|
connection.latestRequest.readyStateChangeController
|
||||||
.add(readyStateChangeEvent);
|
.add(readyStateChangeEvent);
|
||||||
connection.latestRequest.progressController.add(progressEvent);
|
connection.latestRequest.progressController.add(progressEvent);
|
||||||
|
|
@ -339,14 +339,13 @@ void main() {
|
||||||
requestHeaders, (error, _) => fail(error.toString()));
|
requestHeaders, (error, _) => fail(error.toString()));
|
||||||
final data = List<int>.filled(10, 224);
|
final data = List<int>.filled(10, 224);
|
||||||
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
|
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
|
||||||
when(connection.latestRequest.response)
|
when(connection.latestRequest.responseText)
|
||||||
.thenReturn(String.fromCharCodes(frame(data)));
|
.thenReturn(String.fromCharCodes(frame(data)));
|
||||||
|
|
||||||
// Set expectation for request readyState and generate events, so that
|
// Set expectation for request readyState and generate events, so that
|
||||||
// incomingMessages stream completes.
|
// incomingMessages stream completes.
|
||||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
when(connection.latestRequest.readyState).thenReturnInOrder(
|
||||||
when(connection.latestRequest.readyState)
|
[XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE]);
|
||||||
.thenAnswer((_) => readyStates.removeAt(0));
|
|
||||||
connection.latestRequest.readyStateChangeController
|
connection.latestRequest.readyStateChangeController
|
||||||
.add(readyStateChangeEvent);
|
.add(readyStateChangeEvent);
|
||||||
connection.latestRequest.progressController.add(progressEvent);
|
connection.latestRequest.progressController.add(progressEvent);
|
||||||
|
|
@ -371,7 +370,7 @@ void main() {
|
||||||
const errorDetails = 'error details';
|
const errorDetails = 'error details';
|
||||||
when(connection.latestRequest.responseHeaders)
|
when(connection.latestRequest.responseHeaders)
|
||||||
.thenReturn({'content-type': 'application/grpc+proto'});
|
.thenReturn({'content-type': 'application/grpc+proto'});
|
||||||
when(connection.latestRequest.readyState).thenReturn(HttpRequest.DONE);
|
when(connection.latestRequest.readyState).thenReturn(XMLHttpRequest.DONE);
|
||||||
when(connection.latestRequest.responseText).thenReturn(errorDetails);
|
when(connection.latestRequest.responseText).thenReturn(errorDetails);
|
||||||
connection.latestRequest.readyStateChangeController
|
connection.latestRequest.readyStateChangeController
|
||||||
.add(readyStateChangeEvent);
|
.add(readyStateChangeEvent);
|
||||||
|
|
@ -400,12 +399,12 @@ void main() {
|
||||||
|
|
||||||
when(connection.latestRequest.responseHeaders).thenReturn(metadata);
|
when(connection.latestRequest.responseHeaders).thenReturn(metadata);
|
||||||
when(connection.latestRequest.readyState)
|
when(connection.latestRequest.readyState)
|
||||||
.thenReturn(HttpRequest.HEADERS_RECEIVED);
|
.thenReturn(XMLHttpRequest.HEADERS_RECEIVED);
|
||||||
|
|
||||||
// At first invocation the response should be the the first message, after
|
// At first invocation the response should be the the first message, after
|
||||||
// that first + last messages.
|
// that first + last messages.
|
||||||
var first = true;
|
var first = true;
|
||||||
when(connection.latestRequest.response).thenAnswer((_) {
|
when(connection.latestRequest.responseText).thenAnswer((_) {
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
return encodedStrings[0];
|
return encodedStrings[0];
|
||||||
|
|
@ -413,7 +412,7 @@ void main() {
|
||||||
return encodedStrings[0] + encodedStrings[1];
|
return encodedStrings[0] + encodedStrings[1];
|
||||||
});
|
});
|
||||||
|
|
||||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
final readyStates = [XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE];
|
||||||
when(connection.latestRequest.readyState)
|
when(connection.latestRequest.readyState)
|
||||||
.thenAnswer((_) => readyStates.removeAt(0));
|
.thenAnswer((_) => readyStates.removeAt(0));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue