Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit bc52e8f

Browse files
committed
add tests
1 parent 61f83fd commit bc52e8f

File tree

58 files changed

+58128
-1075
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+58128
-1075
lines changed

CodePush.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
+ (NSString *)getApplicationSupportDirectory;
2424

25+
// The below methods are only used during tests.
26+
+ (BOOL)isUsingTestConfiguration;
27+
+ (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration;
28+
+ (void)clearTestUpdates;
29+
2530
@end
2631

2732
@interface CodePushConfig : NSObject
@@ -77,6 +82,10 @@ failCallback:(void (^)(NSError *err))failCallback;
7782

7883
+ (void)rollbackPackage;
7984

85+
// The below methods are only used during tests.
86+
+ (void)downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl;
87+
+ (void)clearTestUpdates;
88+
8089
@end
8190

8291
typedef NS_ENUM(NSInteger, CodePushInstallMode) {

CodePush.js

Lines changed: 74 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -20,75 +20,75 @@ function checkForUpdate(deploymentKey = null) {
2020
* different from the CodePush update they have already installed.
2121
*/
2222
return getConfiguration()
23-
.then((configResult) => {
24-
/*
25-
* If a deployment key was explicitly provided,
26-
* then let's override the one we retrieved
27-
* from the native-side of the app. This allows
28-
* dynamically "redirecting" end-users at different
29-
* deployments (e.g. an early access deployment for insiders).
30-
*/
31-
if (deploymentKey) {
32-
config = Object.assign({}, configResult, { deploymentKey });
33-
} else {
34-
config = configResult;
35-
}
36-
37-
sdk = getSDK(config);
38-
39-
// Allow dynamic overwrite of function. This is only to be used for tests.
40-
return module.exports.getCurrentPackage();
41-
})
42-
.then((localPackage) => {
43-
var queryPackage = { appVersion: config.appVersion };
44-
45-
/*
46-
* If the app has a previously installed update, and that update
47-
* was targetted at the same app version that is currently running,
48-
* then we want to use its package hash to determine whether a new
49-
* release has been made on the server. Otherwise, we only need
50-
* to send the app version to the server, since we are interested
51-
* in any updates for current app store version, regardless of hash.
52-
*/
53-
if (localPackage && localPackage.appVersion && semver.compare(localPackage.appVersion, config.appVersion) === 0) {
54-
queryPackage = localPackage;
55-
}
56-
57-
return new Promise((resolve, reject) => {
58-
sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => {
59-
if (err) {
60-
return reject(err);
61-
}
62-
63-
/*
64-
* There are three cases where checkForUpdate will resolve to null:
65-
* ----------------------------------------------------------------
66-
* 1) The server said there isn't an update. This is the most common case.
67-
* 2) The server said there is an update but it requires a newer binary version.
68-
* This would occur when end-users are running an older app store version than
69-
* is available, and CodePush is making sure they don't get an update that
70-
* potentially wouldn't be compatible with what they are running.
71-
* 3) The server said there is an update, but the update's hash is the same as
72-
* the currently running update. This should _never_ happen, unless there is a
73-
* bug in the server, but we're adding this check just to double-check that the
74-
* client app is resilient to a potential issue with the update check.
75-
*/
76-
if (!update || update.updateAppVersion || (update.packageHash === localPackage.packageHash)) {
77-
return resolve(null);
78-
}
23+
.then((configResult) => {
24+
/*
25+
* If a deployment key was explicitly provided,
26+
* then let's override the one we retrieved
27+
* from the native-side of the app. This allows
28+
* dynamically "redirecting" end-users at different
29+
* deployments (e.g. an early access deployment for insiders).
30+
*/
31+
if (deploymentKey) {
32+
config = Object.assign({}, configResult, { deploymentKey });
33+
} else {
34+
config = configResult;
35+
}
36+
37+
sdk = getSDK(config);
38+
39+
// Allow dynamic overwrite of function. This is only to be used for tests.
40+
return module.exports.getCurrentPackage();
41+
})
42+
.then((localPackage) => {
43+
var queryPackage = { appVersion: config.appVersion };
44+
45+
/*
46+
* If the app has a previously installed update, and that update
47+
* was targetted at the same app version that is currently running,
48+
* then we want to use its package hash to determine whether a new
49+
* release has been made on the server. Otherwise, we only need
50+
* to send the app version to the server, since we are interested
51+
* in any updates for current app store version, regardless of hash.
52+
*/
53+
if (localPackage && localPackage.appVersion && semver.compare(localPackage.appVersion, config.appVersion) === 0) {
54+
queryPackage = localPackage;
55+
}
56+
57+
return new Promise((resolve, reject) => {
58+
sdk.queryUpdateWithCurrentPackage(queryPackage, (err, update) => {
59+
if (err) {
60+
return reject(err);
61+
}
62+
63+
/*
64+
* There are three cases where checkForUpdate will resolve to null:
65+
* ----------------------------------------------------------------
66+
* 1) The server said there isn't an update. This is the most common case.
67+
* 2) The server said there is an update but it requires a newer binary version.
68+
* This would occur when end-users are running an older app store version than
69+
* is available, and CodePush is making sure they don't get an update that
70+
* potentially wouldn't be compatible with what they are running.
71+
* 3) The server said there is an update, but the update's hash is the same as
72+
* the currently running update. This should _never_ happen, unless there is a
73+
* bug in the server, but we're adding this check just to double-check that the
74+
* client app is resilient to a potential issue with the update check.
75+
*/
76+
if (!update || update.updateAppVersion || (update.packageHash === localPackage.packageHash)) {
77+
return resolve(null);
78+
}
7979

80-
update = Object.assign(update, PackageMixins.remote);
81-
82-
NativeCodePush.isFailedUpdate(update.packageHash)
83-
.then((isFailedHash) => {
84-
update.failedInstall = isFailedHash;
85-
resolve(update);
86-
})
87-
.catch(reject)
88-
.done();
89-
})
90-
});
91-
});
80+
update = Object.assign(update, PackageMixins.remote);
81+
82+
NativeCodePush.isFailedUpdate(update.packageHash)
83+
.then((isFailedHash) => {
84+
update.failedInstall = isFailedHash;
85+
resolve(update);
86+
})
87+
.catch(reject)
88+
.done();
89+
})
90+
});
91+
});
9292
}
9393

9494
var getConfiguration = (() => {
@@ -130,11 +130,8 @@ function getCurrentPackage() {
130130
}
131131

132132
function getSDK(config) {
133-
if (testSdk) {
134-
return testSdk;
135-
} else {
136-
return new Sdk(requestFetchAdapter, config);
137-
}
133+
// Allow dynamic overwrite of acquisition SDK. This is only to be used for tests.
134+
return new module.exports.AcquisitionSdk(requestFetchAdapter, config);
138135
}
139136

140137
/* Logs messages to console with the [CodePush] prefix */
@@ -143,16 +140,15 @@ function log(message) {
143140
}
144141

145142
var testConfig;
146-
var testSdk;
147143

148144
// This function is only used for tests. Replaces the default SDK, configuration and native bridge
149-
function setUpTestDependencies(providedTestSdk, providedTestConfig, testNativeBridge) {
150-
if (providedTestSdk) testSdk = providedTestSdk;
145+
function setUpTestDependencies(testSdk, providedTestConfig, testNativeBridge) {
146+
if (testSdk) module.exports.AcquisitionSdk = testSdk;
151147
if (providedTestConfig) testConfig = providedTestConfig;
152148
if (testNativeBridge) NativeCodePush = testNativeBridge;
153149
}
154150

155-
/**
151+
/*
156152
* The sync method provides a simple, one-line experience for
157153
* incorporating the check, download and application of an update.
158154
*
@@ -300,6 +296,7 @@ function sync(options = {}, syncStatusChangeCallback, downloadProgressCallback)
300296
};
301297

302298
var CodePush = {
299+
AcquisitionSdk: Sdk,
303300
checkForUpdate: checkForUpdate,
304301
getConfiguration: getConfiguration,
305302
getCurrentPackage: getCurrentPackage,

CodePush.m

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ @implementation CodePush {
1313

1414
RCT_EXPORT_MODULE()
1515

16-
static NSTimer *_timer;
17-
static BOOL usingTestFolder = NO;
16+
static BOOL testConfigurationFlag = NO;
1817

1918
static NSString *const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
2019
static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
@@ -24,6 +23,8 @@ @implementation CodePush {
2423
static NSString *const PendingUpdateHashKey = @"hash";
2524
static NSString *const PendingUpdateIsLoadingKey = @"isLoading";
2625

26+
id saveTestModule = nil;
27+
2728
@synthesize bridge = _bridge;
2829

2930
// Public Obj-C API (see header for method comments)
@@ -68,6 +69,40 @@ + (NSString *)getApplicationSupportDirectory
6869
return applicationSupportDirectory;
6970
}
7071

72+
/*
73+
* This returns a boolean value indicating whether CodePush has
74+
* been set to run under a test configuration.
75+
*/
76+
+ (BOOL)isUsingTestConfiguration
77+
{
78+
return testConfigurationFlag;
79+
}
80+
81+
/*
82+
* This is used to enable an environment in which tests can be run.
83+
* Specifically, it flips a boolean flag that causes bundles to be
84+
* saved to a test folder and enables the ability to modify
85+
* installed bundles on the fly from JavaScript.
86+
*/
87+
+ (void)setUsingTestConfiguration:(BOOL)shouldUseTestConfiguration
88+
{
89+
testConfigurationFlag = shouldUseTestConfiguration;
90+
}
91+
92+
/*
93+
* This is used to clean up all test updates. It can only be used
94+
* when the testConfigurationFlag is set to YES, otherwise it will
95+
* simply no-op.
96+
*/
97+
+ (void)clearTestUpdates
98+
{
99+
if ([CodePush isUsingTestConfiguration]) {
100+
[CodePushPackage clearTestUpdates];
101+
[self removePendingUpdate];
102+
}
103+
}
104+
105+
71106
// Private API methods
72107

73108
/*
@@ -155,10 +190,12 @@ - (void)loadBundle
155190
// is debugging and therefore, shouldn't be redirected to a local
156191
// file (since Chrome wouldn't support it). Otherwise, update
157192
// the current bundle URL to point at the latest update
158-
if (![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
159-
_bridge.bundleURL = [CodePush bundleURL];
193+
if ([CodePush isUsingTestConfiguration] || ![_bridge.bundleURL.scheme hasPrefix:@"http"]) {
194+
NSURL *url = [CodePush bundleURL];
195+
_bridge.bundleURL = url;
160196
}
161197

198+
saveTestModule = _bridge.modules[@"RCTTestModule"];
162199
[_bridge reload];
163200
});
164201
}
@@ -180,7 +217,7 @@ - (void)rollbackPackage
180217

181218
// Rollback to the previous version and de-register the new update
182219
[CodePushPackage rollbackPackage];
183-
[self removePendingUpdate];
220+
[CodePush removePendingUpdate];
184221
[self loadBundle];
185222
}
186223

@@ -207,10 +244,10 @@ - (void)saveFailedUpdate:(NSString *)packageHash
207244
}
208245

209246
/*
210-
* This method is used to register the fact that a pending
247+
* This method is used to register the fact that a pending
211248
* update succeeded and therefore can be removed.
212249
*/
213-
- (void)removePendingUpdate
250+
+ (void)removePendingUpdate
214251
{
215252
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
216253
[preferences removeObjectForKey:PendingUpdateKey];
@@ -321,9 +358,7 @@ - (void)savePendingUpdate:(NSString *)packageHash
321358
[self savePendingUpdate:updatePackage[@"packageHash"]
322359
isLoading:NO];
323360

324-
if (installMode == CodePushInstallModeImmediate) {
325-
[self loadBundle];
326-
} else if (installMode == CodePushInstallModeOnNextResume) {
361+
if (installMode == CodePushInstallModeOnNextResume) {
327362
// Ensure we do not add the listener twice.
328363
if (!_hasResumeListener) {
329364
// Register for app resume notifications so that we
@@ -376,7 +411,7 @@ - (void)savePendingUpdate:(NSString *)packageHash
376411
RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
377412
rejecter:(RCTPromiseRejectBlock)reject)
378413
{
379-
[self removePendingUpdate];
414+
[CodePush removePendingUpdate];
380415
resolve([NSNull null]);
381416
}
382417

@@ -388,9 +423,16 @@ - (void)savePendingUpdate:(NSString *)packageHash
388423
[self loadBundle];
389424
}
390425

391-
RCT_EXPORT_METHOD(setUsingTestFolder:(BOOL)shouldUseTestFolder)
426+
/*
427+
* This method is the native side of the CodePush.downloadAndReplaceCurrentBundle()
428+
* method, which is only to be used during tests and no-ops if the test configuration
429+
* flag is not set.
430+
*/
431+
RCT_EXPORT_METHOD(downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl)
392432
{
393-
usingTestFolder = shouldUseTestFolder;
433+
if ([CodePush isUsingTestConfiguration]) {
434+
[CodePushPackage downloadAndReplaceCurrentBundle:remoteBundleUrl];
435+
}
394436
}
395437

396438
@end

CodePushPackage.m

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ @implementation CodePushPackage
1414

1515
+ (NSString *)getCodePushPath
1616
{
17-
return [[CodePush getApplicationSupportDirectory] stringByAppendingPathComponent:@"CodePush"];
17+
NSString* codePushPath = [[CodePush getApplicationSupportDirectory] stringByAppendingPathComponent:@"CodePush"];
18+
if ([CodePush isUsingTestConfiguration]) {
19+
codePushPath = [codePushPath stringByAppendingPathComponent:@"TestPackages"];
20+
}
21+
22+
return codePushPath;
1823
}
1924

2025
+ (NSString *)getDownloadFilePath
@@ -481,4 +486,31 @@ + (void)rollbackPackage
481486
[self updateCurrentPackageInfo:info error:&error];
482487
}
483488

489+
+ (void)downloadAndReplaceCurrentBundle:(NSString *)remoteBundleUrl
490+
{
491+
NSURL *urlRequest = [NSURL URLWithString:remoteBundleUrl];
492+
NSError *error = nil;
493+
NSString *downloadedBundle = [NSString stringWithContentsOfURL:urlRequest
494+
encoding:NSUTF8StringEncoding
495+
error:&error];
496+
497+
if (error) {
498+
NSLog(@"Error downloading from URL %@", remoteBundleUrl);
499+
} else {
500+
NSString *currentPackageBundlePath = [self getCurrentPackageBundlePath:&error];
501+
[downloadedBundle writeToFile:currentPackageBundlePath
502+
atomically:YES
503+
encoding:NSUTF8StringEncoding
504+
error:&error];
505+
}
506+
}
507+
508+
+ (void)clearTestUpdates
509+
{
510+
if ([CodePush isUsingTestConfiguration]) {
511+
[[NSFileManager defaultManager] removeItemAtPath:[self getCodePushPath] error:nil];
512+
[[NSFileManager defaultManager] removeItemAtPath:[self getStatusFilePath] error:nil];
513+
}
514+
}
515+
484516
@end

0 commit comments

Comments
 (0)