Add an integration test for WebSocket
Summary: **Motivation** See if we can safely run a WebSocket test in Travis CI Closes https://github.com/facebook/react-native/pull/11433 Differential Revision: D4342024 Pulled By: ericvicenti fbshipit-source-id: 137fb0c39ed7ea3726e2778d5c0bdac4cef6ab89
This commit is contained in:
parent
e0c3d56d3a
commit
a531efe26e
|
@ -18,6 +18,14 @@
|
||||||
[_runner runTest:_cmd module:@#name]; \
|
[_runner runTest:_cmd module:@#name]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define RCT_TEST_ONLY_WITH_PACKAGER(name) \
|
||||||
|
- (void)test##name \
|
||||||
|
{ \
|
||||||
|
if (getenv("CI_USE_PACKAGER")) { \
|
||||||
|
[_runner runTest:_cmd module:@#name]; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
@interface UIExplorerIntegrationTests : XCTestCase
|
@interface UIExplorerIntegrationTests : XCTestCase
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -63,6 +71,7 @@ RCT_TEST(AppEventsTest)
|
||||||
//RCT_TEST(LayoutEventsTest) // Disabled due to flakiness: #8686784
|
//RCT_TEST(LayoutEventsTest) // Disabled due to flakiness: #8686784
|
||||||
RCT_TEST(SimpleSnapshotTest)
|
RCT_TEST(SimpleSnapshotTest)
|
||||||
RCT_TEST(PromiseTest)
|
RCT_TEST(PromiseTest)
|
||||||
|
RCT_TEST_ONLY_WITH_PACKAGER(WebSocketTest)
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -31,6 +31,7 @@ var TESTS = [
|
||||||
require('./SimpleSnapshotTest'),
|
require('./SimpleSnapshotTest'),
|
||||||
require('./ImageSnapshotTest'),
|
require('./ImageSnapshotTest'),
|
||||||
require('./PromiseTest'),
|
require('./PromiseTest'),
|
||||||
|
require('./WebSocketTest'),
|
||||||
];
|
];
|
||||||
|
|
||||||
TESTS.forEach(
|
TESTS.forEach(
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var ReactNative = require('react-native');
|
||||||
|
var { View } = ReactNative;
|
||||||
|
var { TestModule } = ReactNative.NativeModules;
|
||||||
|
|
||||||
|
const DEFAULT_WS_URL = 'ws://localhost:5555/';
|
||||||
|
|
||||||
|
const WS_EVENTS = [
|
||||||
|
'close',
|
||||||
|
'error',
|
||||||
|
'message',
|
||||||
|
'open',
|
||||||
|
];
|
||||||
|
const WS_STATES = [
|
||||||
|
/* 0 */ 'CONNECTING',
|
||||||
|
/* 1 */ 'OPEN',
|
||||||
|
/* 2 */ 'CLOSING',
|
||||||
|
/* 3 */ 'CLOSED',
|
||||||
|
];
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
url: string;
|
||||||
|
fetchStatus: ?string;
|
||||||
|
socket: ?WebSocket;
|
||||||
|
socketState: ?number;
|
||||||
|
lastSocketEvent: ?string;
|
||||||
|
lastMessage: ?string | ?ArrayBuffer;
|
||||||
|
testMessage: string;
|
||||||
|
testExpectedResponse: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WebSocketTest extends React.Component {
|
||||||
|
state: State = {
|
||||||
|
url: DEFAULT_WS_URL,
|
||||||
|
fetchStatus: null,
|
||||||
|
socket: null,
|
||||||
|
socketState: null,
|
||||||
|
lastSocketEvent: null,
|
||||||
|
lastMessage: null,
|
||||||
|
testMessage: 'testMessage',
|
||||||
|
testExpectedResponse: 'testMessage_response'
|
||||||
|
};
|
||||||
|
|
||||||
|
_waitFor = (condition: any, timeout: any, callback: any) => {
|
||||||
|
var remaining = timeout;
|
||||||
|
var t;
|
||||||
|
var timeoutFunction = function() {
|
||||||
|
if (condition()) {
|
||||||
|
callback(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
remaining--;
|
||||||
|
if (remaining === 0) {
|
||||||
|
callback(false);
|
||||||
|
} else {
|
||||||
|
t = setTimeout(timeoutFunction,1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
t = setTimeout(timeoutFunction,1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
_connect = () => {
|
||||||
|
const socket = new WebSocket(this.state.url);
|
||||||
|
WS_EVENTS.forEach(ev => socket.addEventListener(ev, this._onSocketEvent));
|
||||||
|
this.setState({
|
||||||
|
socket,
|
||||||
|
socketState: socket.readyState,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_socketIsConnected = () => {
|
||||||
|
return this.state.socketState === 1; //'OPEN'
|
||||||
|
}
|
||||||
|
|
||||||
|
_socketIsDisconnected = () => {
|
||||||
|
return this.state.socketState === 3; //'CLOSED'
|
||||||
|
}
|
||||||
|
|
||||||
|
_disconnect = () => {
|
||||||
|
if (!this.state.socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state.socket.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onSocketEvent = (event: any) => {
|
||||||
|
const state: any = {
|
||||||
|
socketState: event.target.readyState,
|
||||||
|
lastSocketEvent: event.type,
|
||||||
|
};
|
||||||
|
if (event.type === 'message') {
|
||||||
|
state.lastMessage = event.data;
|
||||||
|
}
|
||||||
|
this.setState(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
_sendText = (text: string) => {
|
||||||
|
if (!this.state.socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state.socket.send(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
_sendTestMessage = () => {
|
||||||
|
this._sendText(this.state.testMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
_receivedTestExpectedResponse = () => {
|
||||||
|
return (this.state.lastMessage === this.state.testExpectedResponse);
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.testConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
testConnect = () => {
|
||||||
|
var component = this;
|
||||||
|
component._connect();
|
||||||
|
component._waitFor(component._socketIsConnected, 5, function(connectSucceeded) {
|
||||||
|
if (!connectSucceeded) {
|
||||||
|
TestModule.markTestPassed(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
component.testSendAndReceive();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testSendAndReceive = () => {
|
||||||
|
var component = this;
|
||||||
|
component._sendTestMessage();
|
||||||
|
component._waitFor(component._receivedTestExpectedResponse, 5, function(messageReceived) {
|
||||||
|
if (!messageReceived) {
|
||||||
|
TestModule.markTestPassed(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
component.testDisconnect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testDisconnect = () => {
|
||||||
|
var component = this;
|
||||||
|
component._disconnect();
|
||||||
|
component._waitFor(component._socketIsDisconnected, 5, function(disconnectSucceeded) {
|
||||||
|
TestModule.markTestPassed(disconnectSucceeded);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.Element<any> {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebSocketTest.displayName = 'WebSocketTest';
|
||||||
|
|
||||||
|
module.exports = WebSocketTest;
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# This source code is licensed under the BSD-style license found in the
|
||||||
|
# LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
# of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
# Set terminal title
|
||||||
|
echo -en "\033]0;Web Socket Test Server\a"
|
||||||
|
clear
|
||||||
|
|
||||||
|
THIS_DIR=$(dirname "$0")
|
||||||
|
pushd "$THIS_DIR"
|
||||||
|
./websocket_integration_test_server.js
|
||||||
|
popd
|
||||||
|
|
||||||
|
echo "Process terminated. Press <enter> to close the window"
|
||||||
|
read
|
|
@ -0,0 +1,52 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2013-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* The examples provided by Facebook are for non-commercial testing and
|
||||||
|
* evaluation purposes only.
|
||||||
|
*
|
||||||
|
* Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
|
||||||
|
console.log(`\
|
||||||
|
WebSocket integration test server
|
||||||
|
|
||||||
|
This will send each incoming message back, with the string '_response' appended.
|
||||||
|
An incoming message of 'exit' will shut down the server.
|
||||||
|
|
||||||
|
`);
|
||||||
|
|
||||||
|
const server = new WebSocket.Server({port: 5555});
|
||||||
|
server.on('connection', (ws) => {
|
||||||
|
ws.on('message', (message) => {
|
||||||
|
console.log('Received message:', message);
|
||||||
|
if (message === 'exit') {
|
||||||
|
console.log('WebSocket integration test server exit');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
console.log('Cookie:', ws.upgradeReq.headers.cookie);
|
||||||
|
ws.send(message + '_response');
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.send('hello');
|
||||||
|
});
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
# Start the packager and preload the UIExplorerApp bundle for better performance in integration tests
|
# Start the packager and preload the UIExplorerApp bundle for better performance in integration tests
|
||||||
open "./packager/launchPackager.command" || echo "Can't start packager automatically"
|
open "./packager/launchPackager.command" || echo "Can't start packager automatically"
|
||||||
|
open "./IntegrationTests/launchWebSocketServer.command" || echo "Can't start web socket server automatically"
|
||||||
sleep 20
|
sleep 20
|
||||||
curl 'http://localhost:8081/Examples/UIExplorer/js/UIExplorerApp.ios.bundle?platform=ios&dev=true' -o temp.bundle
|
curl 'http://localhost:8081/Examples/UIExplorer/js/UIExplorerApp.ios.bundle?platform=ios&dev=true' -o temp.bundle
|
||||||
rm temp.bundle
|
rm temp.bundle
|
||||||
|
@ -23,8 +24,10 @@ function cleanup {
|
||||||
WATCHMAN_LOGS=/usr/local/Cellar/watchman/3.1/var/run/watchman/$USER.log
|
WATCHMAN_LOGS=/usr/local/Cellar/watchman/3.1/var/run/watchman/$USER.log
|
||||||
[ -f $WATCHMAN_LOGS ] && cat $WATCHMAN_LOGS
|
[ -f $WATCHMAN_LOGS ] && cat $WATCHMAN_LOGS
|
||||||
fi
|
fi
|
||||||
# kill whatever is occupying port 8081
|
# kill whatever is occupying port 8081 (packager)
|
||||||
lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
|
lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
|
||||||
|
# kill whatever is occupying port 5555 (web socket server)
|
||||||
|
lsof -i tcp:5555 | awk 'NR!=1 {print $2}' | xargs kill
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue