add tests

This commit is contained in:
Geoffrey Goh
2015-12-18 00:08:11 -08:00
parent 61f83fde0d
commit bc52e8f0c5
58 changed files with 58127 additions and 1074 deletions

View File

@@ -22,6 +22,11 @@
+ (NSString *)getApplicationSupportDirectory;
// The below methods are only used during tests.
+ (BOOL)isUsingTestConfiguration;
+ (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration;
+ (void)clearTestUpdates;
@end
@interface CodePushConfig : NSObject
@@ -77,6 +82,10 @@ failCallback:(void (^)(NSError *err))failCallback;
+ (void)rollbackPackage;
// The below methods are only used during tests.
+ (void)downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl;
+ (void)clearTestUpdates;
@end
typedef NS_ENUM(NSInteger, CodePushInstallMode) {

View File

@@ -20,75 +20,75 @@ function checkForUpdate(deploymentKey = null) {
* different from the CodePush update they have already installed.
*/
return getConfiguration()
.then((configResult) => {
/*
* If a deployment key was explicitly provided,
* then let's override the one we retrieved
* from the native-side of the app. This allows
* dynamically "redirecting" end-users at different
* deployments (e.g. an early access deployment for insiders).
*/
if (deploymentKey) {
config = Object.assign({}, configResult, { deploymentKey });
} else {
config = configResult;
}
sdk = getSDK(config);
// Allow dynamic overwrite of function. This is only to be used for tests.
return module.exports.getCurrentPackage();
})
.then((localPackage) => {
var queryPackage = { appVersion: config.appVersion };
/*
* If the app has a previously installed update, and that update
* was targetted at the same app version that is currently running,
* then we want to use its package hash to determine whether a new
* release has been made on the server. Otherwise, we only need
* to send the app version to the server, since we are interested
* in any updates for current app store version, regardless of hash.
*/
if (localPackage && localPackage.appVersion && semver.compare(localPackage.appVersion, config.appVersion) === 0) {
queryPackage = localPackage;
}
return new Promise((resolve, reject) => {
sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => {
if (err) {
return reject(err);
}
/*
* There are three cases where checkForUpdate will resolve to null:
* ----------------------------------------------------------------
* 1) The server said there isn't an update. This is the most common case.
* 2) The server said there is an update but it requires a newer binary version.
* This would occur when end-users are running an older app store version than
* is available, and CodePush is making sure they don't get an update that
* potentially wouldn't be compatible with what they are running.
* 3) The server said there is an update, but the update's hash is the same as
* the currently running update. This should _never_ happen, unless there is a
* bug in the server, but we're adding this check just to double-check that the
* client app is resilient to a potential issue with the update check.
*/
if (!update || update.updateAppVersion || (update.packageHash === localPackage.packageHash)) {
return resolve(null);
}
.then((configResult) => {
/*
* If a deployment key was explicitly provided,
* then let's override the one we retrieved
* from the native-side of the app. This allows
* dynamically "redirecting" end-users at different
* deployments (e.g. an early access deployment for insiders).
*/
if (deploymentKey) {
config = Object.assign({}, configResult, { deploymentKey });
} else {
config = configResult;
}
sdk = getSDK(config);
// Allow dynamic overwrite of function. This is only to be used for tests.
return module.exports.getCurrentPackage();
})
.then((localPackage) => {
var queryPackage = { appVersion: config.appVersion };
/*
* If the app has a previously installed update, and that update
* was targetted at the same app version that is currently running,
* then we want to use its package hash to determine whether a new
* release has been made on the server. Otherwise, we only need
* to send the app version to the server, since we are interested
* in any updates for current app store version, regardless of hash.
*/
if (localPackage && localPackage.appVersion && semver.compare(localPackage.appVersion, config.appVersion) === 0) {
queryPackage = localPackage;
}
return new Promise((resolve, reject) => {
sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => {
if (err) {
return reject(err);
}
/*
* There are three cases where checkForUpdate will resolve to null:
* ----------------------------------------------------------------
* 1) The server said there isn't an update. This is the most common case.
* 2) The server said there is an update but it requires a newer binary version.
* This would occur when end-users are running an older app store version than
* is available, and CodePush is making sure they don't get an update that
* potentially wouldn't be compatible with what they are running.
* 3) The server said there is an update, but the update's hash is the same as
* the currently running update. This should _never_ happen, unless there is a
* bug in the server, but we're adding this check just to double-check that the
* client app is resilient to a potential issue with the update check.
*/
if (!update || update.updateAppVersion || (update.packageHash === localPackage.packageHash)) {
return resolve(null);
}
update = Object.assign(update, PackageMixins.remote);
NativeCodePush.isFailedUpdate(update.packageHash)
.then((isFailedHash) => {
update.failedInstall = isFailedHash;
resolve(update);
})
.catch(reject)
.done();
})
});
});
update = Object.assign(update, PackageMixins.remote);
NativeCodePush.isFailedUpdate(update.packageHash)
.then((isFailedHash) => {
update.failedInstall = isFailedHash;
resolve(update);
})
.catch(reject)
.done();
})
});
});
}
var getConfiguration = (() => {
@@ -130,11 +130,8 @@ function getCurrentPackage() {
}
function getSDK(config) {
if (testSdk) {
return testSdk;
} else {
return new Sdk(requestFetchAdapter, config);
}
// Allow dynamic overwrite of acquisition SDK. This is only to be used for tests.
return new module.exports.AcquisitionSdk(requestFetchAdapter, config);
}
/* Logs messages to console with the [CodePush] prefix */
@@ -143,16 +140,15 @@ function log(message) {
}
var testConfig;
var testSdk;
// This function is only used for tests. Replaces the default SDK, configuration and native bridge
function setUpTestDependencies(providedTestSdk, providedTestConfig, testNativeBridge) {
if (providedTestSdk) testSdk = providedTestSdk;
function setUpTestDependencies(testSdk, providedTestConfig, testNativeBridge) {
if (testSdk) module.exports.AcquisitionSdk = testSdk;
if (providedTestConfig) testConfig = providedTestConfig;
if (testNativeBridge) NativeCodePush = testNativeBridge;
}
/**
/*
* The sync method provides a simple, one-line experience for
* incorporating the check, download and application of an update.
*
@@ -300,6 +296,7 @@ function sync(options = {}, syncStatusChangeCallback, downloadProgressCallback)
};
var CodePush = {
AcquisitionSdk: Sdk,
checkForUpdate: checkForUpdate,
getConfiguration: getConfiguration,
getCurrentPackage: getCurrentPackage,

View File

@@ -13,8 +13,7 @@
RCT_EXPORT_MODULE()
static NSTimer *_timer;
static BOOL usingTestFolder = NO;
static BOOL testConfigurationFlag = NO;
static NSString *const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
@@ -24,6 +23,8 @@ static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
static NSString *const PendingUpdateHashKey = @"hash";
static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
id saveTestModule = nil;
@synthesize bridge = _bridge;
// Public Obj-C API (see header for method comments)
@@ -68,6 +69,40 @@ static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
return applicationSupportDirectory;
}
/*
* This returns a boolean value indicating whether CodePush has
* been set to run under a test configuration.
*/
+ (BOOL)isUsingTestConfiguration
{
return testConfigurationFlag;
}
/*
* This is used to enable an environment in which tests can be run.
* Specifically, it flips a boolean flag that causes bundles to be
* saved to a test folder and enables the ability to modify
* installed bundles on the fly from JavaScript.
*/
+ (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration
{
testConfigurationFlag = shouldUseTestConfiguration;
}
/*
* This is used to clean up all test updates. It can only be used
* when the testConfigurationFlag is set to YES, otherwise it will
* simply no-op.
*/
+ (void)clearTestUpdates
{
if ([CodePush isUsingTestConfiguration]) {
[CodePushPackage clearTestUpdates];
[self removePendingUpdate];
}
}
// Private API methods
/*
@@ -155,10 +190,12 @@ static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
// is debugging and therefore, shouldn't be redirected to a local
// file (since Chrome wouldn't support it). Otherwise, update
// the current bundle URL to point at the latest update
if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
_bridge.bundleURL = [CodePush bundleURL];
if ([CodePush isUsingTestConfiguration] || ![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
NSURL *url = [CodePush bundleURL];
_bridge.bundleURL = url;
}
saveTestModule = _bridge.modules[@"RCTTestModule"];
[_bridge reload];
});
}
@@ -180,7 +217,7 @@ static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
// Rollback to the previous version and de-register the new update
[CodePushPackage rollbackPackage];
[self removePendingUpdate];
[CodePush removePendingUpdate];
[self loadBundle];
}
@@ -207,10 +244,10 @@ static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
}
/*
* This method is used to register the fact that a pending
* This method is used to register the fact that a pending
* update succeeded and therefore can be removed.
*/
- (void)removePendingUpdate
+ (void)removePendingUpdate
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
[preferences removeObjectForKey:PendingUpdateKey];
@@ -321,9 +358,7 @@ RCT_EXPORT_METHOD(installUpdate:(NSDictionary*)updatePackage
[self savePendingUpdate:updatePackage[@"packageHash"]
isLoading:NO];
if (installMode == CodePushInstallModeImmediate) {
[self loadBundle];
} else if (installMode == CodePushInstallModeOnNextResume) {
if (installMode == CodePushInstallModeOnNextResume) {
// Ensure we do not add the listener twice.
if (!_hasResumeListener) {
// Register for app resume notifications so that we
@@ -376,7 +411,7 @@ RCT_EXPORT_METHOD(isFirstRun:(NSString *)packageHash
RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self removePendingUpdate];
[CodePush removePendingUpdate];
resolve([NSNull null]);
}
@@ -388,9 +423,16 @@ RCT_EXPORT_METHOD(restartApp)
[self loadBundle];
}
RCT_EXPORT_METHOD(setUsingTestFolder:(BOOL)shouldUseTestFolder)
/*
* This method is the native side of the CodePush.downloadAndReplaceCurrentBundle()
* method, which is only to be used during tests and no-ops if the test configuration
* flag is not set.
*/
RCT_EXPORT_METHOD(downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl)
{
usingTestFolder = shouldUseTestFolder;
if ([CodePush isUsingTestConfiguration]) {
[CodePushPackage downloadAndReplaceCurrentBundle:remoteBundleUrl];
}
}
@end

View File

@@ -14,7 +14,12 @@ NSString * const UnzippedFolderName = @"unzipped";
+ (NSString *)getCodePushPath
{
return [[CodePush getApplicationSupportDirectory] stringByAppendingPathComponent:@"CodePush"];
NSString* codePushPath = [[CodePush getApplicationSupportDirectory] stringByAppendingPathComponent:@"CodePush"];
if ([CodePush isUsingTestConfiguration]) {
codePushPath = [codePushPath stringByAppendingPathComponent:@"TestPackages"];
}
return codePushPath;
}
+ (NSString *)getDownloadFilePath
@@ -481,4 +486,31 @@ NSString * const UnzippedFolderName = @"unzipped";
[self updateCurrentPackageInfo:info error:&error];
}
+ (void)downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl
{
NSURL *urlRequest = [NSURL URLWithString:remoteBundleUrl];
NSError *error = nil;
NSString *downloadedBundle = [NSString stringWithContentsOfURL:urlRequest
encoding:NSUTF8StringEncoding
error:&error];
if (error) {
NSLog(@"Error downloading from URL %@", remoteBundleUrl);
} else {
NSString *currentPackageBundlePath = [self getCurrentPackageBundlePath:&error];
[downloadedBundle writeToFile:currentPackageBundlePath
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
}
}
+ (void)clearTestUpdates
{
if ([CodePush isUsingTestConfiguration]) {
[[NSFileManager defaultManager] removeItemAtPath:[self getCodePushPath] error:nil];
[[NSFileManager defaultManager] removeItemAtPath:[self getStatusFilePath] error:nil];
}
}
@end

View File

@@ -0,0 +1,70 @@
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <RCTTest/RCTTestRunner.h>
#import "RCTAssert.h"
#import "CodePush.h"
#define FB_REFERENCE_IMAGE_DIR "\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""
@interface CheckForUpdateTests : XCTestCase
@end
@implementation CheckForUpdateTests
{
RCTTestRunner *_runner;
}
- (void)setUp
{
#if __LP64__
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
[CodePush setUsingTestConfiguration:YES];
[CodePush clearTestUpdates];
_runner = RCTInitRunnerForApp(@"CodePushDemoAppTests/CheckForUpdateTests/CheckForUpdateTestApp", nil);
}
#pragma mark Logic Tests
- (void)testFirstUpdate
{
[_runner runTest:_cmd
module:@"FirstUpdateTest"];
}
- (void)testNewUpdate
{
[_runner runTest:_cmd
module:@"NewUpdateTest"];
}
- (void)testNoRemotePackage
{
[_runner runTest:_cmd module:@"NoRemotePackageTest"];
}
- (void)testRemotePackageAppVersionNewer
{
[_runner runTest:_cmd
module:@"RemotePackageAppVersionNewerTest"];
}
- (void)testSamePackage
{
[_runner runTest:_cmd
module:@"SamePackageTest"];
}
- (void)testSwitchDeploymentKey
{
[_runner runTest:_cmd
module:@"SwitchDeploymentKeyTest"];
}
@end

View File

@@ -1,6 +1,6 @@
'use strict';
"use strict";
var React = require('react-native');
var React = require("react-native");
var {
AppRegistry,
@@ -12,24 +12,25 @@ var {
} = React;
var TESTS = [
require('./NoRemotePackageTest'),
require('./NoRemotePackageWithSameAppVersionTest'),
require('./FirstUpdateTest'),
require('./NewUpdateTest'),
require('./SamePackageTest')
require("./testcases/FirstUpdateTest"),
require("./testcases/NewUpdateTest"),
require("./testcases/NoRemotePackageTest"),
require("./testcases/RemotePackageAppVersionNewerTest"),
require("./testcases/SamePackageTest"),
require("./testcases/SwitchDeploymentKeyTest")
];
TESTS.forEach(
(test) => AppRegistry.registerComponent(test.displayName, () => test)
);
var QueryUpdateTestApp = React.createClass({
getInitialState: function() {
var CheckForUpdateTestApp = React.createClass({
getInitialState() {
return {
test: null,
};
},
render: function() {
render() {
if (this.state.test) {
return (
<ScrollView>
@@ -40,9 +41,7 @@ var QueryUpdateTestApp = React.createClass({
return (
<View style={styles.container}>
<Text style={styles.row}>
Click on a test to run it in this shell for easier debugging and
development. Run all tests in the testing environment with cmd+U in
Xcode.
CheckForUpdate Tests
</Text>
<View style={styles.separator} />
<ScrollView>
@@ -53,6 +52,9 @@ var QueryUpdateTestApp = React.createClass({
<Text style={styles.testName}>
{test.displayName}
</Text>
<Text style={styles.testDescription}>
{test.description}
</Text>
</TouchableOpacity>,
<View style={styles.separator} />
])}
@@ -64,7 +66,7 @@ var QueryUpdateTestApp = React.createClass({
var styles = StyleSheet.create({
container: {
backgroundColor: 'white',
backgroundColor: "white",
marginTop: 40,
margin: 15,
},
@@ -72,12 +74,15 @@ var styles = StyleSheet.create({
padding: 10,
},
testName: {
fontWeight: '500',
fontWeight: "500",
},
testDescription: {
fontSize: 10
},
separator: {
height: 1,
backgroundColor: '#bbbbbb',
backgroundColor: "#bbbbbb",
}
});
AppRegistry.registerComponent('QueryUpdateTestApp', () => QueryUpdateTestApp);
AppRegistry.registerComponent("CheckForUpdateTestApp", () => CheckForUpdateTestApp);

View File

@@ -0,0 +1,48 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var serverPackage = {
appVersion: "1.5.0",
description: "Angry flappy birds",
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
isAvailable: true,
isMandatory: false,
packageHash: "hash240",
packageSize: 1024,
updateAppVersion: false
};
var localPackage = {};
var FirstUpdateTest = createTestCaseComponent(
"FirstUpdateTest",
"should return an update when called from freshly installed binary if the server has one",
() => {
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage, localPackage);
var mockConfiguration = { appVersion : "1.5.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
return Promise.resolve();
},
() => {
return CodePush.checkForUpdate()
.then((update) => {
if (update) {
assert.deepEqual(update, Object.assign(serverPackage, PackageMixins.remote));
} else {
throw new Error("checkForUpdate did not return the update from the server");
}
});
}
);
module.exports = FirstUpdateTest;

View File

@@ -0,0 +1,58 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var serverPackage = {
appVersion: "1.5.0",
description: "Angry flappy birds",
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
isAvailable: true,
isMandatory: false,
packageHash: "hash240",
packageSize: 1024,
updateAppVersion: false
};
var localPackage = {
downloadURL: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash123",
packageSize: 1024
};
var NewUpdateTest = createTestCaseComponent(
"NewUpdateTest",
"should return an update when server has a package that is newer than the current one",
() => {
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage, localPackage);
var mockConfiguration = { appVersion : "1.5.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
return Promise.resolve();
},
() => {
return CodePush.checkForUpdate()
.then((update) => {
if (update) {
assert.deepEqual(update, Object.assign(serverPackage, PackageMixins.remote));
} else {
throw new Error("checkForUpdate did not return the update from the server");
}
});
}
);
module.exports = NewUpdateTest;

View File

@@ -0,0 +1,36 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var serverPackage = null;
var localPackage = {};
var NoRemotePackageTest = createTestCaseComponent(
"NoRemotePackageTest",
"should not return an update when the server has none",
() => {
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage, localPackage);
var mockConfiguration = { appVersion : "1.5.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
return Promise.resolve();
},
() => {
return CodePush.checkForUpdate()
.then((update) => {
if (update) {
throw new Error("checkForUpdate should not return an update if there is none on the server");
}
});
}
);
module.exports = NoRemotePackageTest;

View File

@@ -0,0 +1,47 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var serverPackage = {
appVersion: "1.5.0",
description: "",
downloadUrl: "",
isAvailable: false,
isMandatory: false,
packageHash: "",
updateAppVersion: true
};
var localPackage = {};
var RemotePackageAppVersionNewerTest = createTestCaseComponent(
"RemotePackageAppVersionNewerTest",
"should drop the update when the server reports one with a newer binary version",
() => {
return new Promise((resolve, reject) => {
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage, localPackage);
var mockConfiguration = { appVersion : "1.0.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
resolve();
});
},
() => {
return CodePush.checkForUpdate()
.then((update) => {
if (update) {
throw new Error("checkForUpdate should not return an update if remote package is of a different binary version");
}
});
}
);
module.exports = RemotePackageAppVersionNewerTest;

View File

@@ -0,0 +1,46 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var serverPackage = {
appVersion: "1.5.0",
description: "Angry flappy birds",
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
isAvailable: true,
isMandatory: false,
packageHash: "hash240",
packageSize: 1024,
updateAppVersion: false
};
var localPackage = serverPackage;
var SamePackageTest = createTestCaseComponent(
"SamePackageTest",
"should not return an update when the server's version is the same as the local version",
() => {
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage, localPackage);
var mockConfiguration = { appVersion : "1.5.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
return Promise.resolve();
},
() => {
return CodePush.checkForUpdate()
.then((update) => {
if (update) {
throw new Error("checkForUpdate should not return a package when local package is identical");
}
});
}
);
module.exports = SamePackageTest;

View File

@@ -0,0 +1,48 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var serverPackage = {
appVersion: "1.5.0",
description: "Angry flappy birds",
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
isAvailable: true,
isMandatory: false,
packageHash: "hash240",
packageSize: 1024,
updateAppVersion: false
};
var localPackage = {};
var deploymentKey = "myKey123";
var SwitchDeploymentKeyTest = createTestCaseComponent(
"SwitchDeploymentKeyTest",
"should check for an update under the specified deployment key",
() => {
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage, localPackage, deploymentKey);
var mockConfiguration = { appVersion : "1.5.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
return Promise.resolve();
},
() => {
return CodePush.checkForUpdate(deploymentKey)
.then((update) => {
if (update) {
assert.deepEqual(update, Object.assign(serverPackage, PackageMixins.remote));
} else {
throw new Error("checkForUpdate did not return the update from the server");
}
});
}
);
module.exports = SwitchDeploymentKeyTest;

View File

@@ -3,6 +3,7 @@
#import <RCTTest/RCTTestRunner.h>
#import "RCTAssert.h"
#import "CodePush.h"
#define FB_REFERENCE_IMAGE_DIR "\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""
@@ -12,25 +13,27 @@
@implementation DownloadProgressTests
{
RCTTestRunner *_runner;
RCTTestRunner *_runner;
}
- (void)setUp
{
#if __LP64__
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTestApp", nil);
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
[CodePush setUsingTestConfiguration:YES];
[CodePush clearTestUpdates];
_runner = RCTInitRunnerForApp(@"CodePushDemoAppTests/DownloadProgressTests/DownloadProgressTestApp", nil);
}
#pragma mark Logic Tests
- (void)testDownloadProgress
{
[_runner runTest:_cmd module:@"DownloadProgressTest"];
[_runner runTest:_cmd module:@"DownloadProgressTest"];
}
@end

View File

@@ -1,101 +0,0 @@
"use strict";
var React = require("react-native");
var { Platform, DeviceEventEmitter } = require("react-native");
var CodePushSdk = require("react-native-code-push");
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
Text,
View,
} = React;
var DownloadProgressTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.runTest();
}
},
checkReceivedAndExpectedBytesEqual() {
if (this.state.progress.receivedBytes !== this.state.progress.totalBytes) {
throw new Error("Bytes do not tally: Received bytes=" + this.state.progress.receivedBytes + " Total bytes=" + this.state.progress.totalBytes);
}
},
runTest() {
var downloadProgressSubscription = DeviceEventEmitter.addListener(
"CodePushDownloadProgress",
(progress) => {
this.setState({
progress:progress,
done: false,
});
}
);
var updates = require("./TestPackages");
NativeCodePush.downloadUpdate(updates.smallPackage)
.then((smallPackage) => {
if (smallPackage) {
this.checkReceivedAndExpectedBytesEqual();
return NativeCodePush.downloadUpdate(updates.mediumPackage);
} else {
throw new Error("Small package download failed.");
}
})
.then((mediumPackage) => {
if (mediumPackage) {
this.checkReceivedAndExpectedBytesEqual();
return NativeCodePush.downloadUpdate(updates.largePackage);
} else {
throw new Error("Medium package download failed.");
}
})
.done((largePackage) => {
if (largePackage) {
this.checkReceivedAndExpectedBytesEqual();
this.setState({done: true}, RCTTestModule.markTestCompleted);
} else {
throw new Error("Large package download failed.");
}
});
},
render() {
var progressView;
if (this.state.progress) {
progressView = (
<Text>{this.state.progress.receivedBytes} of {this.state.progress.totalBytes} bytes received</Text>
);
}
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
{this.constructor.displayName + ": "}
{this.state.done ? "Done" : "Testing..."}
</Text>
{progressView}
</View>
);
}
});
DownloadProgressTest.displayName = "DownloadProgressTest";
module.exports = DownloadProgressTest;

View File

@@ -12,7 +12,7 @@ var {
} = React;
var TESTS = [
require("./DownloadProgressTest")
require("./testcases/DownloadProgressTest")
];
TESTS.forEach(
@@ -20,12 +20,12 @@ TESTS.forEach(
);
var DownloadProgressTestApp = React.createClass({
getInitialState: function() {
getInitialState() {
return {
test: null,
};
},
render: function() {
render() {
if (this.state.test) {
return (
<ScrollView>
@@ -36,9 +36,7 @@ var DownloadProgressTestApp = React.createClass({
return (
<View style={styles.container}>
<Text style={styles.row}>
Click on a test to run it in this shell for easier debugging and
development. Run all tests in the testing environment with cmd+U in
Xcode.
DownloadProgress Tests
</Text>
<View style={styles.separator} />
<ScrollView>
@@ -49,6 +47,9 @@ var DownloadProgressTestApp = React.createClass({
<Text style={styles.testName}>
{test.displayName}
</Text>
<Text style={styles.testDescription}>
{test.description}
</Text>
</TouchableOpacity>,
<View style={styles.separator} />
])}

View File

@@ -1,7 +1,7 @@
var { Platform } = require("react-native");
var packages = {
smallPackage: {
var packages = [
{
downloadUrl: "smallFile",
description: "Angry flappy birds",
appVersion: "1.5.0",
@@ -12,7 +12,7 @@ var packages = {
packageHash: "hash240",
packageSize: 1024
},
mediumPackage: {
{
downloadUrl: "mediumFile",
description: "Angry flappy birds",
appVersion: "1.5.0",
@@ -23,7 +23,7 @@ var packages = {
packageHash: "hash240",
packageSize: 1024
},
largePackage: {
{
downloadUrl: "largeFile",
description: "Angry flappy birds",
appVersion: "1.5.0",
@@ -34,15 +34,14 @@ var packages = {
packageHash: "hash240",
packageSize: 1024
}
};
];
for (var aPackage in packages) {
packages.forEach((aPackage) => {
if (Platform.OS === "android") {
// Genymotion forwards 10.0.3.2 to host machine's localhost
packages[aPackage].downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/DownloadProgressTests/" + packages[aPackage].downloadUrl;
aPackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/DownloadProgressTests/resources/" + aPackage.downloadUrl;
} else if (Platform.OS === "ios") {
packages[aPackage].downloadUrl = "http://localhost:8081/CodePushDemoAppTests/DownloadProgressTests/" + packages[aPackage].downloadUrl;
aPackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/DownloadProgressTests/resources/" + aPackage.downloadUrl;
}
}
});
module.exports = packages;

View File

@@ -0,0 +1,52 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var testPackages = require("../resources/TestPackages");
var localPackage = {};
var saveProgress;
function checkReceivedAndExpectedBytesEqual() {
assert(saveProgress, "Download progress was not reported.");
assert.equal(
saveProgress.receivedBytes,
saveProgress.totalBytes,
`Bytes do not tally: Received bytes=${saveProgress.receivedBytes} Total bytes=${saveProgress.totalBytes}`
);
console.log("Downloaded one package.");
saveProgress = null;
}
var DownloadProgressTest = createTestCaseComponent(
"DownloadProgressTest",
"should successfully download all the bytes contained in the test packages",
() => {
testPackages.forEach((aPackage, index) => {
testPackages[index] = Object.assign(aPackage, PackageMixins.remote);
});
return Promise.resolve();
},
() => {
var downloadProgressCallback = (downloadProgress) => {
console.log(`Expecting ${downloadProgress.totalBytes} bytes, received ${downloadProgress.receivedBytes} bytes.`);
saveProgress = downloadProgress;
};
// Chains promises together.
return testPackages.reduce((aPackageDownloaded, nextPackage, index) => {
return aPackageDownloaded
.then(() => {
// Skip the first time.
index && checkReceivedAndExpectedBytesEqual();
return nextPackage.download(downloadProgressCallback);
})
}, Promise.resolve());
}
);
module.exports = DownloadProgressTest;

View File

@@ -3,6 +3,9 @@
#import <RCTTest/RCTTestRunner.h>
#import "RCTAssert.h"
#import "RCTRootView.h"
#import "RCTText.h"
#import "CodePush.h"
#define FB_REFERENCE_IMAGE_DIR "\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""
@@ -12,25 +15,92 @@
@implementation InstallUpdateTests
{
RCTTestRunner *_runner;
RCTTestRunner *_runner;
}
- (void)setUp
{
#if __LP64__
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"CodePushDemoAppTests/InstallUpdateTests/InstallUpdateTestApp", nil);
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
[CodePush setUsingTestConfiguration:YES];
}
#pragma mark Logic Tests
- (void)testDownloadAndInstallUpdate
- (void)testInstallModeImmediate
{
[self runTest:@"InstallModeImmediateTest"];
}
- (void)testInstallModeOnNextResume
{
[self runTest:@"InstallModeOnNextResumeTest"];
}
- (void)testInstallModeOnNextRestart
{
[self runTest:@"InstallModeOnNextRestartTest"];
}
- (void)testIsFirstRun
{
[self runTest:@"IsFirstRunTest"];
}
- (void)testNotifyApplicationReady
{
[self runTest:@"NotifyApplicationReadyTest"];
}
- (void)testRollback
{
[self runTest:@"RollbackTest"];
}
- (void)testIsFailedUpdate
{
[self runTest:@"IsFailedUpdateTest"];
}
- (void)runTest:(NSString *)testName
{
[CodePush clearTestUpdates];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/testcases/%@.bundle?platform=ios&dev=true", testName]]
moduleName:testName
initialProperties:nil
launchOptions:nil];
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
vc.view = [UIView new];
[vc.view addSubview:rootView];
while (![self foundTestPassedText:vc.view]) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
}
- (BOOL)foundTestPassedText:(UIView *)view {
BOOL foundText = NO;
NSArray *subviews = [view subviews];
if ([subviews count] == 0) {
if ([view isKindOfClass:[RCTText class]] && [[((RCTText *)view) textStorage].string isEqualToString:@"Test Passed!"]) {
return YES;
}
return NO;
}
[_runner runTest:_cmd module:@"DownloadAndInstallUpdateTest"];
for (UIView *subview in subviews) {
foundText = [self foundTestPassedText:subview];
if (foundText) {
break;
}
}
return foundText;
}
@end

View File

@@ -1,66 +0,0 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
View,
} = React;
var RCTTestModule = require('NativeModules').TestModule;
var NativeCodePush = require('react-native').NativeModules.CodePush;
var CodePushDemoApp = React.createClass({
componentDidMount: function() {
NativeCodePush.setUsingTestFolder(true);
NativeCodePush.getCurrentPackage().then(
(savedPackage) => {
if (savedPackage) {
var testPackage = require("./TestPackage");
for (var key in testPackage) {
if (savedPackage[key] !== testPackage[key]) {
throw new Error("The local package is still different from the updated package after installation");
}
}
} else {
throw new Error("The updated package was not saved");
}
},
(err) => {
throw new Error("The updated package was not saved");
}
);
},
render: function() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
If you see this, you have successfully installed an update!
</Text>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
CodePushDemoApp.displayName = 'CodePushDemoApp';
AppRegistry.registerComponent('CodePushDemoApp', () => CodePushDemoApp);

View File

@@ -1,72 +0,0 @@
'use strict';
var React = require('react-native');
var CodePushSdk = require('react-native-code-push');
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
AppRegistry,
Text,
View,
} = React;
var DownloadAndInstallUpdateTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.setUp();
this.runTest();
}
},
setUp(callWhenDone) {
var mockConfiguration = { appVersion : "1.5.0" };
NativeCodePush.setUsingTestFolder(true);
CodePushSdk.setUpTestDependencies(null, mockConfiguration, NativeCodePush);
},
runTest() {
var update = require("./TestPackage");
NativeCodePush.downloadUpdate(update).done((downloadedPackage) => {
NativeCodePush.installUpdate(downloadedPackage, /*rollbackTimeout*/ 1000, CodePushSdk.InstallMode.IMMEDIATE)
.then(() => {
CodePushSdk.getCurrentPackage().then((localPackage) => {
if (localPackage.packageHash == update.packageHash) {
this.setState({done: true}, RCTTestModule.markTestCompleted);
} else {
throw new Error("Update was not installed");
}
});
});
});
},
render() {
return (
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
});
DownloadAndInstallUpdateTest.displayName = 'DownloadAndInstallUpdateTest';
AppRegistry.registerComponent('CodePushDemoApp', () => DownloadAndInstallUpdateTest);
module.exports = DownloadAndInstallUpdateTest;

View File

@@ -1,79 +0,0 @@
'use strict';
var React = require('react-native');
var {
AppRegistry,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} = React;
var TESTS = [
require('./DownloadAndInstallUpdateTest')
];
TESTS.forEach(
(test) => AppRegistry.registerComponent(test.displayName, () => test)
);
var InstallUpdateTestApp = React.createClass({
getInitialState: function() {
return {
test: null,
};
},
render: function() {
if (this.state.test) {
return (
<ScrollView>
<this.state.test />
</ScrollView>
);
}
return (
<View style={styles.container}>
<Text style={styles.row}>
Click on a test to run it in this shell for easier debugging and
development. Run all tests in the testing environment with cmd+U in
Xcode.
</Text>
<View style={styles.separator} />
<ScrollView>
{TESTS.map((test) => [
<TouchableOpacity
onPress={() => this.setState({test})}
style={styles.row}>
<Text style={styles.testName}>
{test.displayName}
</Text>
</TouchableOpacity>,
<View style={styles.separator} />
])}
</ScrollView>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
backgroundColor: 'white',
marginTop: 40,
margin: 15,
},
row: {
padding: 10,
},
testName: {
fontWeight: '500',
},
separator: {
height: 1,
backgroundColor: '#bbbbbb',
}
});
AppRegistry.registerComponent('InstallUpdateTestApp', () => InstallUpdateTestApp);

View File

@@ -1,21 +0,0 @@
var { Platform } = require("react-native");
var testPackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
if (Platform.OS === "android") {
// Genymotion forwards 10.0.3.2 to host machine's localhost
testPackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/CodePushDemoApp.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
testPackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/CodePushDemoApp.includeRequire.runModule.bundle?platform=ios&dev=true"
}
module.exports = testPackage;

View File

@@ -0,0 +1,42 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var {
AppRegistry,
Text,
View,
} = React;
var IsFirstRunTest = React.createClass({
getInitialState() {
return {};
},
componentDidMount() {
CodePush.getCurrentPackage()
.then((localPackage) => {
if (localPackage.isFirstRun) {
this.setState({ passed: true });
} else {
this.setState({ passed: false });
}
});
},
render() {
var text = "Testing...";
if (this.state.passed !== undefined) {
text = this.state.passed ? "Test Passed!" : "Test Failed!";
}
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
{text}
</Text>
</View>
);
}
});
AppRegistry.registerComponent("IsFirstRunTest", () => IsFirstRunTest);

View File

@@ -0,0 +1,73 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var createMockAcquisitionSdk = require("../../utils/mockAcquisitionSdk");
var {
AppRegistry,
Platform,
Text,
View,
} = React;
var IsFailedUpdateTest = React.createClass({
getInitialState() {
return {};
},
componentDidMount() {
var serverPackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash241",
packageSize: 1024
};
if (Platform.OS === "android") {
serverPackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/IsFailedUpdateTestBundleV2.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
serverPackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/IsFailedUpdateTestBundleV2.includeRequire.runModule.bundle?platform=ios&dev=true"
}
var mockAcquisitionSdk = createMockAcquisitionSdk(serverPackage);
var mockConfiguration = { appVersion : "1.5.0" };
CodePush.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePush.notifyApplicationReady()
.then(() => {
return CodePush.checkForUpdate();
})
.then((remotePackage) => {
if (remotePackage.failedInstall) {
this.setState({ passed: true });
} else {
return remotePackage.download();
}
})
.then((localPackage) => {
return localPackage && localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
render() {
var text = "Testing...";
if (this.state.passed !== undefined) {
text = this.state.passed ? "Test Passed!" : "Test Failed!";
}
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
{text}
</Text>
</View>
);
}
});
AppRegistry.registerComponent("IsFailedUpdateTest", () => IsFailedUpdateTest);

View File

@@ -0,0 +1,27 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var {
AppRegistry,
Text,
View,
} = React;
var IsFailedUpdateTest = React.createClass({
componentDidMount() {
CodePush.restartApp();
},
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Testing...
</Text>
</View>
);
}
});
AppRegistry.registerComponent("IsFailedUpdateTest", () => IsFailedUpdateTest);

View File

@@ -0,0 +1,43 @@
"use strict";
var React = require("react-native");
var { Platform } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var RCTTestModule = React.NativeModules.TestModule;
var {
AppRegistry,
Text,
View,
} = React;
var NotifyApplicationReadyTest = React.createClass({
getInitialState() {
return {};
},
componentDidMount() {
CodePush.notifyApplicationReady()
.then(() => {
if (Platform.OS === "android") {
return NativeCodePush.downloadAndReplaceCurrentBundle("http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassNotifyApplicationReadyTest.includeRequire.runModule.bundle?platform=android&dev=true");
} else if (Platform.OS === "ios") {
return NativeCodePush.downloadAndReplaceCurrentBundle("http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassNotifyApplicationReadyTest.includeRequire.runModule.bundle?platform=ios&dev=true");
}
})
.then(() => {
CodePush.restartApp();
});
},
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Testing...
</Text>
</View>
);
}
});
AppRegistry.registerComponent("NotifyApplicationReadyTest", () => NotifyApplicationReadyTest);

View File

@@ -0,0 +1,23 @@
"use strict";
var React = require("react-native");
var {
AppRegistry,
Text,
View,
} = React;
var InstallModeImmediateTest = React.createClass({
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Test Passed!
</Text>
</View>
);
}
});
AppRegistry.registerComponent("InstallModeImmediateTest", () => InstallModeImmediateTest);

View File

@@ -0,0 +1,23 @@
"use strict";
var React = require("react-native");
var {
AppRegistry,
Text,
View,
} = React;
var InstallModeOnNextRestartTest = React.createClass({
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Test Passed!
</Text>
</View>
);
}
});
AppRegistry.registerComponent("InstallModeOnNextRestartTest", () => InstallModeOnNextRestartTest);

View File

@@ -0,0 +1,23 @@
"use strict";
var React = require("react-native");
var {
AppRegistry,
Text,
View,
} = React;
var InstallModeOnNextResumeTest = React.createClass({
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Test Passed!
</Text>
</View>
);
}
});
AppRegistry.registerComponent("InstallModeOnNextResumeTest", () => InstallModeOnNextResumeTest);

View File

@@ -0,0 +1,23 @@
"use strict";
var React = require("react-native");
var {
AppRegistry,
Text,
View,
} = React;
var NotifyApplicationReadyTest = React.createClass({
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Test Passed!
</Text>
</View>
);
}
});
AppRegistry.registerComponent("NotifyApplicationReadyTest", () => NotifyApplicationReadyTest);

View File

@@ -0,0 +1,61 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var RCTTestModule = React.NativeModules.TestModule;
var NativeCodePush = React.NativeModules.CodePush;
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var {
AppRegistry,
Platform,
Text,
View,
} = React;
var RollbackTest = React.createClass({
componentDidMount() {
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash241",
packageSize: 1024
};
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV2.includeRequire.runModule.bundle?platform=android&dev=true"
CodePush.notifyApplicationReady()
.then(() => {
NativeCodePush.downloadAndReplaceCurrentBundle("http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1Pass.includeRequire.runModule.bundle?platform=android&dev=true");
});
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV2.includeRequire.runModule.bundle?platform=ios&dev=true"
CodePush.notifyApplicationReady()
.then(() => {
NativeCodePush.downloadAndReplaceCurrentBundle("http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1Pass.includeRequire.runModule.bundle?platform=ios&dev=true");
});
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Testing...
</Text>
</View>
);
}
});
AppRegistry.registerComponent("RollbackTest", () => RollbackTest);

View File

@@ -0,0 +1,23 @@
"use strict";
var React = require("react-native");
var {
AppRegistry,
Text,
View,
} = React;
var RollbackTest = React.createClass({
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Test Passed!
</Text>
</View>
);
}
});
AppRegistry.registerComponent("RollbackTest", () => RollbackTest);

View File

@@ -0,0 +1,27 @@
"use strict";
var React = require("react-native");
var CodePush = require("react-native-code-push");
var {
AppRegistry,
Text,
View,
} = React;
var RollbackTest = React.createClass({
componentDidMount() {
CodePush.restartApp();
},
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
Testing...
</Text>
</View>
);
}
});
AppRegistry.registerComponent("RollbackTest", () => RollbackTest);

View File

@@ -0,0 +1,44 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var InstallModeImmediateTest = createTestCaseComponent(
"InstallModeImmediateTest",
"App should restart immediately to the new version after it is installed",
() => {
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeImmediateTest.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeImmediateTest.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("InstallModeImmediateTest", () => InstallModeImmediateTest);

View File

@@ -0,0 +1,48 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var InstallModeOnNextRestartTest = createTestCaseComponent(
"InstallModeOnNextRestartTest",
"App should boot up the new version after it is installed and restarted",
() => {
if (Platform.OS === "android") {
// Genymotion forwards 10.0.3.2 to host machine's localhost
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeOnNextRestartTest.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeOnNextRestartTest.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeOnNextRestart);
})
.then(() => {
CodePush.restartApp();
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("InstallModeOnNextRestartTest", () => InstallModeOnNextRestartTest);

View File

@@ -0,0 +1,47 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var InstallModeOnNextResumeTest = createTestCaseComponent(
"InstallModeOnNextResumeTest",
"App should boot up the new version after it is installed and resumed",
() => {
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeOnNextResumeTest.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeOnNextResumeTest.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeOnNextResume);
})
.then(() => {
CodePush.restartApp();
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("InstallModeOnNextResumeTest", () => InstallModeOnNextResumeTest);

View File

@@ -0,0 +1,44 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var IsFailedUpdateTest = createTestCaseComponent(
"IsFailedUpdateTest",
"After an installed update is rolled back, checkForUpdate should return a package with the failedInstall property set to \"true\"",
() => {
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/IsFailedUpdateTestBundleV1.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/IsFailedUpdateTestBundleV1.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("IsFailedUpdateTest", () => IsFailedUpdateTest);

View File

@@ -0,0 +1,44 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var IsFirstRunTest = createTestCaseComponent(
"IsFirstRunTest",
"After the app is installed, the isFirstRun property on the current package should be set to \"true\"",
() => {
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/CheckIsFirstRunAndPassTest.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/CheckIsFirstRunAndPassTest.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("IsFirstRunTest", () => IsFirstRunTest);

View File

@@ -0,0 +1,44 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var NotifyApplicationReadyTest = createTestCaseComponent(
"NotifyApplicationReadyTest",
"After an update, the app should remain using the installed version after multiple restarts if \"notifyApplicationReady\" is called.",
() => {
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/NotifyApplicationReadyAndRestart.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/NotifyApplicationReadyAndRestart.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("NotifyApplicationReadyTest", () => NotifyApplicationReadyTest);

View File

@@ -0,0 +1,44 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter, Platform, AppRegistry } = require("react-native");
var CodePush = require("react-native-code-push");
var NativeCodePush = React.NativeModules.CodePush;
var createTestCaseComponent = require("../../utils/createTestCaseComponent");
var PackageMixins = require("react-native-code-push/package-mixins.js")(NativeCodePush);
var assert = require("assert");
var remotePackage = {
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var RollbackTest = createTestCaseComponent(
"RollbackTest",
"should successfully rollback if \"notifyApplicationReady\" is not called in the installed package.",
() => {
if (Platform.OS === "android") {
remotePackage.downloadUrl = "http://10.0.3.2:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.includeRequire.runModule.bundle?platform=android&dev=true"
} else if (Platform.OS === "ios") {
remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.includeRequire.runModule.bundle?platform=ios&dev=true"
}
remotePackage = Object.assign(remotePackage, PackageMixins.remote);
return Promise.resolve();
},
() => {
remotePackage.download()
.then((localPackage) => {
return localPackage.install(NativeCodePush.codePushInstallModeImmediate);
});
},
/*passAfterRun*/ false
);
AppRegistry.registerComponent("RollbackTest", () => RollbackTest);

View File

@@ -1,61 +0,0 @@
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <RCTTest/RCTTestRunner.h>
#import "RCTAssert.h"
#define FB_REFERENCE_IMAGE_DIR "\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\""
@interface QueryUpdateTests : XCTestCase
@end
@implementation QueryUpdateTests
{
RCTTestRunner *_runner;
}
- (void)setUp
{
#if __LP64__
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"CodePushDemoAppTests/QueryUpdateTests/QueryUpdateTestApp", nil);
}
#pragma mark Logic Tests
- (void)testNoRemotePackage
{
[_runner runTest:_cmd module:@"NoRemotePackageTest"];
}
- (void)testNoRemotePackageWithSameAppVersion
{
[_runner runTest:_cmd
module:@"NoRemotePackageWithSameAppVersionTest"];
}
- (void)testFirstUpdate
{
[_runner runTest:_cmd
module:@"FirstUpdateTest"];
}
- (void)testNewUpdate
{
[_runner runTest:_cmd
module:@"NewUpdateTest"];
}
- (void)testSamePackage
{
[_runner runTest:_cmd
module:@"SamePackageTest"];
}
@end

View File

@@ -1,96 +0,0 @@
'use strict';
var RCTTestModule = require('NativeModules').TestModule;
var React = require('react-native');
var CodePushSdk = require('react-native-code-push');
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
Text,
View,
} = React;
var FirstUpdateTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.setUp(this.runTest);
}
},
setUp(callWhenDone) {
var mockAcquisitionSdk = {
latestPackage: {
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
},
queryUpdateWithCurrentPackage: function(queryPackage, callback){
if (!this.latestPackage || queryPackage.appVersion !== this.latestPackage.appVersion ||
queryPackage.packageHash == this.latestPackage.packageHash) {
callback(/*err:*/ null, false);
} else {
callback(/*err:*/ null, this.latestPackage);
}
}
};
var mockConfiguration = { appVersion : "1.5.0" };
NativeCodePush.setUsingTestFolder(true);
CodePushSdk.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePushSdk.getCurrentPackage = function () {
return Promise.resolve(null);
}
callWhenDone();
},
runTest() {
CodePushSdk.checkForUpdate().then(
(update) => {
if (update) {
this.setState({done: true}, RCTTestModule.markTestCompleted);
} else {
throw new Error('SDK should return a package when there is an update');
}
},
(err) => {
throw new Error(err.message);
},
);
},
render() {
return (
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
});
FirstUpdateTest.displayName = 'FirstUpdateTest';
module.exports = FirstUpdateTest;

View File

@@ -1,108 +0,0 @@
'use strict';
var RCTTestModule = require('NativeModules').TestModule;
var React = require('react-native');
var CodePushSdk = require('react-native-code-push');
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
Text,
View,
} = React;
var NewUpdateTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.setUp(this.runTest);
}
},
setUp(callWhenDone) {
var mockAcquisitionSdk = {
latestPackage: {
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
},
queryUpdateWithCurrentPackage: function(queryPackage, callback){
if (!this.latestPackage || queryPackage.appVersion !== this.latestPackage.appVersion ||
queryPackage.packageHash == this.latestPackage.packageHash) {
callback(/*err:*/ null, false);
} else {
callback(/*err:*/ null, this.latestPackage);
}
}
};
var localPackage = {
downloadURL: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash123",
packageSize: 1024
};
var mockConfiguration = { appVersion : "1.5.0" };
NativeCodePush.setUsingTestFolder(true);
CodePushSdk.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePushSdk.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
callWhenDone();
},
runTest() {
CodePushSdk.checkForUpdate().then(
(update) => {
if (update) {
this.setState({done: true}, RCTTestModule.markTestCompleted);
} else {
throw new Error('SDK should return a package when there is a new update');
}
},
(err) => {
throw new Error(err.message);
},
);
},
render() {
return (
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
});
NewUpdateTest.displayName = 'NewUpdateTest';
module.exports = NewUpdateTest;

View File

@@ -1,81 +0,0 @@
'use strict';
var React = require('react-native');
var CodePushSdk = require('react-native-code-push');
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
Text,
View,
} = React;
var NoRemotePackageTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.setUp();
this.runTest();
}
},
setUp() {
var mockAcquisitionSdk = {
latestPackage: null,
queryUpdateWithCurrentPackage: function(queryPackage, callback){
if (!this.latestPackage || queryPackage.appVersion !== this.latestPackage.appVersion ||
queryPackage.packageHash == this.latestPackage.packageHash) {
callback(/*err:*/ null, false);
} else {
callback(/*err:*/ null, latestPackage);
}
}
};
var mockConfiguration = { appVersion : "1.5.0" };
NativeCodePush.setUsingTestFolder(true);
CodePushSdk.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
},
runTest() {
CodePushSdk.checkForUpdate().then(
(update) => {
if (update) {
throw new Error('SDK should not return a package if remote does not contain a package');
} else {
this.setState({done: true}, RCTTestModule.markTestCompleted);
}
},
(err) => {
throw new Error(err.message);
},
);
},
render() {
return (
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
});
NoRemotePackageTest.displayName = 'NoRemotePackageTest';
module.exports = NoRemotePackageTest;

View File

@@ -1,108 +0,0 @@
'use strict';
var React = require('react-native');
var RCTTestModule = React.NativeModules.TestModule;
var CodePushSdk = require('react-native-code-push');
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
Text,
View,
} = React;
var NoRemotePackageWithSameAppVersionTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.setUp(this.runTest);
}
},
setUp(callWhenDone) {
var mockAcquisitionSdk = {
latestPackage: {
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
},
queryUpdateWithCurrentPackage: function(queryPackage, callback){
if (!this.latestPackage || queryPackage.appVersion !== this.latestPackage.appVersion ||
queryPackage.packageHash == this.latestPackage.packageHash) {
callback(/*err:*/ null, false);
} else {
callback(/*err:*/ null, latestPackage);
}
}
};
NativeCodePush.setUsingTestFolder(true);
var localPackage = {
downloadURL: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.0.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash123",
packageSize: 1024
};
var mockConfiguration = { appVersion : "1.0.0" };
CodePushSdk.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePushSdk.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
callWhenDone();
},
runTest() {
CodePushSdk.checkForUpdate().then(
(update) => {
if (update) {
throw new Error('SDK should not return a package if remote package is of a different version');
} else {
this.setState({done: true}, RCTTestModule.markTestCompleted);
}
},
(err) => {
throw new Error(err.message);
},
);
},
render() {
return (
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
});
NoRemotePackageWithSameAppVersionTest.displayName = 'NoRemotePackageWithSameAppVersionTest';
module.exports = NoRemotePackageWithSameAppVersionTest;

View File

@@ -1,108 +0,0 @@
'use strict';
var RCTTestModule = require('NativeModules').TestModule;
var React = require('react-native');
var CodePushSdk = require('react-native-code-push');
var NativeCodePush = require("react-native").NativeModules.CodePush;
var RCTTestModule = require('NativeModules').TestModule || {};
var {
Text,
View,
} = React;
var SamePackageTest = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
if (this.props.waitOneFrame) {
requestAnimationFrame(this.runTest);
} else {
this.setUp(this.runTest);
}
},
setUp(callWhenDone) {
var mockAcquisitionSdk = {
latestPackage: {
downloadUrl: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
},
queryUpdateWithCurrentPackage: function(queryPackage, callback){
if (!this.latestPackage || queryPackage.appVersion !== this.latestPackage.appVersion ||
queryPackage.packageHash == this.latestPackage.packageHash) {
callback(/*err:*/ null, false);
} else {
callback(/*err:*/ null, this.latestPackage);
}
}
};
var localPackage = {
downloadURL: "http://www.windowsazure.com/blobs/awperoiuqpweru",
description: "Angry flappy birds",
appVersion: "1.5.0",
label: "2.4.0",
isMandatory: false,
isAvailable: true,
updateAppVersion: false,
packageHash: "hash240",
packageSize: 1024
};
var mockConfiguration = { appVersion : "1.5.0" };
NativeCodePush.setUsingTestFolder(true);
CodePushSdk.setUpTestDependencies(mockAcquisitionSdk, mockConfiguration, NativeCodePush);
CodePushSdk.getCurrentPackage = function () {
return Promise.resolve(localPackage);
}
callWhenDone();
},
runTest() {
CodePushSdk.checkForUpdate().then(
(update) => {
if (update) {
throw new Error('SDK should not return a package when local package is identical');
} else {
this.setState({done: true}, RCTTestModule.markTestCompleted);
}
},
(err) => {
throw new Error(err.message);
}
);
},
render() {
return (
<View style={{backgroundColor: 'white', padding: 40}}>
<Text>
{this.constructor.displayName + ': '}
{this.state.done ? 'Done' : 'Testing...'}
</Text>
</View>
);
}
});
SamePackageTest.displayName = 'SamePackageTest';
module.exports = SamePackageTest;

View File

@@ -1,7 +1,7 @@
Test Cases
---
* QueryUpdateTests - Tests the functionality of querying for new app updates via the SDK
* CheckForUpdateTests - Tests the functionality of querying for new app updates via the SDK
* testNoRemotePackage - Checks that when the remote server has no update packages available, CodePushSdk.queryUpdate does not return a new package nor throw an error.
* testNoRemotePackageWithSameAppVersion - Checks that when the remote server has an update with a different appVersion, the CodePushSdk.queryUpdate does not return a new package nor throw an error.
* testFirstUpdate - Checks that when there is no current package (for example, the current build is a fresh install from the app store) and the remote server has a new package, CodePushSdk.queryUpdate returns that new package without throwing an error.

View File

@@ -0,0 +1,56 @@
"use strict";
var React = require("react-native");
var { DeviceEventEmitter } = require("react-native");
var NativeCodePush = React.NativeModules.CodePush;
// RCTTestModule is not implemented yet for RN Android.
var RCTTestModule = React.NativeModules.TestModule || {};
var {
Text,
View,
} = React;
function createTestCaseComponent(displayName, description, setUp, runTest, passAfterRun = true) {
var TestCaseComponent = React.createClass({
propTypes: {
shouldThrow: React.PropTypes.bool,
waitOneFrame: React.PropTypes.bool,
},
getInitialState() {
return {
done: false,
};
},
componentDidMount() {
setUp()
.then(runTest)
.then(() => {
if (passAfterRun) {
this.setState({done: true}, RCTTestModule.markTestCompleted);
}
})
.catch((err) => {
console.error(err);
throw err;
});
},
render() {
return (
<View style={{backgroundColor: "white", padding: 40}}>
<Text>
{this.state.done ? "Done" : "Testing..."}
</Text>
</View>
);
}
});
TestCaseComponent.displayName = displayName;
TestCaseComponent.description = description;
return TestCaseComponent;
}
module.exports = createTestCaseComponent;

View File

@@ -0,0 +1,19 @@
var assert = require("assert");
function createMockAcquisitionSdk(serverPackage, localPackage, expectedDeploymentKey) {
var AcquisitionManager = function (httpRequester, configuration) {
expectedDeploymentKey && assert.equal(expectedDeploymentKey, configuration.deploymentKey, "checkForUpdate did not initialize Acquisition SDK with the expected deployment key");
}
AcquisitionManager.prototype.queryUpdateWithCurrentPackage = function (queryPackage, callback) {
if (localPackage) {
localPackage.appVersion = queryPackage.appVersion;
assert.deepEqual(queryPackage, localPackage, "checkForUpdate did not attach current package info to the acquisition request");
}
callback(/*err:*/ null, serverPackage);
};
return AcquisitionManager;
}
module.exports = createMockAcquisitionSdk;

File diff suppressed because it is too large Load Diff

View File

@@ -17,9 +17,6 @@ android {
}
}
buildTypes {
debug {
buildConfigField "String", "RUN_TEST", "\"\""
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

View File

@@ -29,29 +29,10 @@ public class MainActivity extends FragmentActivity implements DefaultHardwareBac
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(getApplication())
.setJSBundleFile(codePush.getBundleUrl("index.android.bundle"));
.setJSBundleFile(codePush.getBundleUrl("index.android.bundle"))
.setJSMainModuleName("index.android");
String mainComponentName = null;
switch (BuildConfig.RUN_TEST) {
case "DOWNLOAD_PROGRESS":
builder = builder.setJSMainModuleName(TEST_FOLDER_PREFIX + "DownloadProgressTests/DownloadProgressTestApp");
mainComponentName = "DownloadProgressTestApp";
break;
case "INSTALL_UPDATE":
builder = builder.setJSMainModuleName(TEST_FOLDER_PREFIX + "InstallUpdateTests/InstallUpdateTestApp");
mainComponentName = "InstallUpdateTestApp";
break;
case "QUERY_UPDATE":
builder = builder.setJSMainModuleName(TEST_FOLDER_PREFIX + "QueryUpdateTests/QueryUpdateTestApp");
mainComponentName = "QueryUpdateTestApp";
break;
default:
// Run the standard demo app.
builder = builder.setJSMainModuleName("index.android");
mainComponentName = "CodePushDemoApp";
break;
}
String mainComponentName = "CodePushDemoApp";
mReactInstanceManager = builder.addPackage(new MainReactPackage())
.addPackage(codePush.getReactPackage())

View File

@@ -16,10 +16,7 @@ var Button = require("react-native-button");
var CodePush = require('react-native-code-push');
var CodePushDemoApp = React.createClass({
componentDidMount: function() {
},
sync: function() {
sync() {
var self = this;
CodePush.sync(
{
@@ -83,10 +80,10 @@ var CodePushDemoApp = React.createClass({
CodePush.log(error);
});
},
getInitialState: function() {
getInitialState() {
return { };
},
render: function() {
render() {
var syncView;
var syncButton;
var progressView;

View File

@@ -21,8 +21,9 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
542D39D41C22CED700D4B648 /* libCodePush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81551E0F1B3B427200F5B9F1 /* libCodePush.a */; };
544161591B8BCA81000D9E25 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
5451ACBA1B86A5B600E2A7DF /* QueryUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */; };
5451ACBA1B86A5B600E2A7DF /* CheckForUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5451ACB81B86A5B600E2A7DF /* CheckForUpdateTests.m */; };
5451ACEC1B86E40A00E2A7DF /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5451ACEB1B86E34300E2A7DF /* libRCTTest.a */; };
54D774BA1B87DAF800F2ABF8 /* InstallUpdateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D774B91B87DAF800F2ABF8 /* InstallUpdateTests.m */; };
54F5F2B41BF6B45D007C3CEA /* DownloadProgressTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */; };
@@ -149,7 +150,7 @@
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = CodePushDemoApp/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = CodePushDemoApp/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QueryUpdateTests.m; sourceTree = "<group>"; };
5451ACB81B86A5B600E2A7DF /* CheckForUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CheckForUpdateTests.m; sourceTree = "<group>"; };
5451ACE61B86E34300E2A7DF /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = "../node_modules/react-native/Libraries/RCTTest/RCTTest.xcodeproj"; sourceTree = "<group>"; };
54D774B91B87DAF800F2ABF8 /* InstallUpdateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstallUpdateTests.m; sourceTree = "<group>"; };
54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadProgressTests.m; sourceTree = "<group>"; };
@@ -163,6 +164,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
542D39D41C22CED700D4B648 /* libCodePush.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -241,7 +243,7 @@
isa = PBXGroup;
children = (
54F5F2B31BF6B45D007C3CEA /* DownloadProgressTests.m */,
5451ACB81B86A5B600E2A7DF /* QueryUpdateTests.m */,
5451ACB81B86A5B600E2A7DF /* CheckForUpdateTests.m */,
54D774B91B87DAF800F2ABF8 /* InstallUpdateTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
@@ -628,7 +630,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5451ACBA1B86A5B600E2A7DF /* QueryUpdateTests.m in Sources */,
5451ACBA1B86A5B600E2A7DF /* CheckForUpdateTests.m in Sources */,
54F5F2B41BF6B45D007C3CEA /* DownloadProgressTests.m in Sources */,
54D774BA1B87DAF800F2ABF8 /* InstallUpdateTests.m in Sources */,
);
@@ -678,6 +680,13 @@
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../../..",
"$(SRCROOT)/../node_modules/react-native/Libraries/Text/",
);
INFOPLIST_FILE = ../CodePushDemoAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -695,6 +704,13 @@
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../../..",
"$(SRCROOT)/../node_modules/react-native/Libraries/Text/",
);
INFOPLIST_FILE = ../CodePushDemoAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";

View File

@@ -9,5 +9,8 @@
"react-native": "0.15.0",
"react-native-button": "^1.2.0",
"react-native-code-push": "file:../../"
},
"devDependencies": {
"assert": "^1.3.0"
}
}