Merge pull request #96 from Microsoft/mandatoryInstallMode
Test improvements and mandatory install mode
This commit is contained in:
Коммит
ae66e60158
11
README.md
11
README.md
|
@ -373,6 +373,8 @@ While the `sync` method tries to make it easy to perform silent and active updat
|
|||
|
||||
- __installMode__ *(InstallMode)* - Used to specify the [InstallMode](#installmode) used for the install operation. Defaults to `InstallMode.ON_NEXT_RESTART`.
|
||||
|
||||
- __mandatoryInstallMode__ *(InstallMode)* - Used to specify the [InstallMode](#installmode) used for the install operation if the package is mandatory. Defaults to `InstallMode.IMMEDIATE`.
|
||||
|
||||
- __minimumBackgroundDuration__: If __installMode__ is `InstallMode.ON_NEXT_RESUME`, used to specify the amount of time the app must be in the background before the update is installed when it is resumed. Defaults to `0`.
|
||||
|
||||
- __ignoreFailedUpdates__ *(Boolean)* - Optional boolean flag. If set, updates available on the server for which and update was attempted and rolled back will be ignored. Defaults to `true`.
|
||||
|
@ -577,6 +579,8 @@ Otherwise, the install operation will be marked as failed, and the application i
|
|||
|
||||
- __installMode__: Used to specify the [InstallMode](#installmode) used for the install operation. Defaults to `InstallMode.ON_NEXT_RESTART`.
|
||||
|
||||
- __mandatoryInstallMode__: Used to specify the [InstallMode](#installmode) used for the install operation if the package is mandatory. Defaults to `InstallMode.IMMEDIATE`.
|
||||
|
||||
- __minimumBackgroundDuration__: If __installMode__ is `InstallMode.ON_NEXT_RESUME`, used to specify the amount of time the app must be in the background before the update is installed when it is resumed. Defaults to `0`.
|
||||
|
||||
Example Usage:
|
||||
|
@ -593,8 +597,9 @@ var onInstallSuccess = function () {
|
|||
};
|
||||
|
||||
var onPackageDownloaded = function (localPackage) {
|
||||
// Install the update after someone navigates away from the app for more than 2 minutes
|
||||
localPackage.install(onInstallSuccess, onError, { installMode: InstallMode.ON_NEXT_RESUME, minimumBackgroundDuration: 120 });
|
||||
// Install regular updates after someone navigates away from the app for more than 2 minutes
|
||||
// Install mandatory updates after someone restarts the app
|
||||
localPackage.install(onInstallSuccess, onError, { installMode: InstallMode.ON_NEXT_RESUME, minimumBackgroundDuration: 120, mandatoryInstallMode: InstallMode.ON_NEXT_RESTART });
|
||||
};
|
||||
|
||||
var onUpdateCheck = function (remotePackage) {
|
||||
|
@ -620,7 +625,7 @@ window.codePush.checkForUpdate(onUpdateCheck, onError);
|
|||
|
||||
var app = {
|
||||
onDeviceReady: function () {
|
||||
// Calling this function is requirede during the first application run after an update.
|
||||
// Calling this function is required during the first application run after an update.
|
||||
// If not called, the application will be reverted to the previous version.
|
||||
window.codePush.notifyApplicationReady();
|
||||
// ...
|
||||
|
|
|
@ -255,6 +255,7 @@ var CodePush = (function () {
|
|||
ignoreFailedUpdates: true,
|
||||
installMode: InstallMode.ON_NEXT_RESTART,
|
||||
minimumBackgroundDuration: 0,
|
||||
mandatoryInstallMode: InstallMode.IMMEDIATE,
|
||||
updateDialog: false,
|
||||
deploymentKey: undefined
|
||||
};
|
||||
|
|
|
@ -90,12 +90,15 @@ var LocalPackage = (function (_super) {
|
|||
else {
|
||||
var invokeSuccessAndInstall = function () {
|
||||
CodePushUtil.logMessage("Install succeeded.");
|
||||
if (installOptions.installMode === InstallMode.IMMEDIATE) {
|
||||
var installModeToUse = _this.isMandatory ? installOptions.mandatoryInstallMode : installOptions.installMode;
|
||||
if (installModeToUse === InstallMode.IMMEDIATE) {
|
||||
installSuccess && installSuccess();
|
||||
cordova.exec(function () { }, function () { }, "CodePush", "install", [deployDir.fullPath, installOptions.installMode.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
cordova.exec(function () { }, function () { }, "CodePush", "install", [deployDir.fullPath,
|
||||
installModeToUse.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
}
|
||||
else {
|
||||
cordova.exec(function () { installSuccess && installSuccess(); }, function () { installError && installError(); }, "CodePush", "install", [deployDir.fullPath, installOptions.installMode.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
cordova.exec(function () { installSuccess && installSuccess(); }, function () { installError && installError(); }, "CodePush", "install", [deployDir.fullPath,
|
||||
installModeToUse.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
}
|
||||
};
|
||||
var preInstallSuccess = function () {
|
||||
|
@ -355,7 +358,8 @@ var LocalPackage = (function (_super) {
|
|||
if (!LocalPackage.DefaultInstallOptions) {
|
||||
LocalPackage.DefaultInstallOptions = {
|
||||
installMode: InstallMode.ON_NEXT_RESTART,
|
||||
minimumBackgroundDuration: 0
|
||||
minimumBackgroundDuration: 0,
|
||||
mandatoryInstallMode: InstallMode.IMMEDIATE
|
||||
};
|
||||
}
|
||||
return LocalPackage.DefaultInstallOptions;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="cordova-plugin-code-push" version="1.6.0-beta">
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="cordova-plugin-code-push" version="1.7.0-beta">
|
||||
<name>CodePush</name>
|
||||
<description>This plugin allows you to push code updates to your apps instantly using the CodePush service</description>
|
||||
<license>MIT</license>
|
||||
|
@ -7,8 +7,8 @@
|
|||
<repo>https://github.com/Microsoft/cordova-plugin-code-push.git</repo>
|
||||
|
||||
<dependency id="code-push" version=">=1.8.0-beta" />
|
||||
<dependency id="cordova-plugin-file-transfer" version=">=1.3.0" />
|
||||
<dependency id="cordova-plugin-file" version=">=3.0.0" />
|
||||
<dependency id="cordova-plugin-file" version=">=3.0.0" />
|
||||
<dependency id="cordova-plugin-file-transfer" version=">=1.3.0" />
|
||||
<dependency id="cordova-plugin-zip" version=">=3.0.0" />
|
||||
<dependency id="cordova-plugin-dialogs" version=">=1.1.1" />
|
||||
<dependency id="cordova-plugin-device" version=">=1.1.0" />
|
||||
|
|
|
@ -41,17 +41,27 @@ export interface IEmulatorManager {
|
|||
/**
|
||||
* Launches an already installed application by app id.
|
||||
*/
|
||||
launchInstalledApplication(appId: string): Q.Promise<void>;
|
||||
launchInstalledApplication(appId: string): Q.Promise<string>;
|
||||
|
||||
/**
|
||||
* Ends a running application given its app id.
|
||||
*/
|
||||
endRunningApplication(appId: string): Q.Promise<string>;
|
||||
|
||||
/**
|
||||
* Restarts an already installed application by app id.
|
||||
*/
|
||||
restartApplication(appId: string): Q.Promise<void>;
|
||||
restartApplication(appId: string): Q.Promise<string>;
|
||||
|
||||
/**
|
||||
* Navigates away from the current app, waits for a delay, then navigates to the specified app.
|
||||
* Navigates away from the current app, waits for a delay (defaults to 1 second), then navigates to the specified app.
|
||||
*/
|
||||
resumeApplication(appId: string, delayBeforeResumingMs: number): Q.Promise<void>;
|
||||
resumeApplication(appId: string, delayBeforeResumingMs: number): Q.Promise<string>;
|
||||
|
||||
/**
|
||||
* Prepares the emulator for a test.
|
||||
*/
|
||||
prepareEmulatorForTest(appId: string): Q.Promise<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,17 +140,17 @@ export class IOSEmulatorManager implements IEmulatorManager {
|
|||
/**
|
||||
* Launches an already installed application by app id.
|
||||
*/
|
||||
public launchInstalledApplication(appId: string): Q.Promise<void> {
|
||||
return tu.TestUtil.getProcessOutput("xcrun simctl launch booted " + appId, undefined, true)
|
||||
.then<void>(output => { console.log(output); });
|
||||
launchInstalledApplication(appId: string): Q.Promise<string> {
|
||||
return tu.TestUtil.getProcessOutput("xcrun simctl launch booted " + appId, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a running application, given its Cordova app id.
|
||||
* Ends a running application given its app id.
|
||||
*/
|
||||
private endRunningApplication(appId: string): Q.Promise<void> {
|
||||
return tu.TestUtil.getProcessOutput("xcrun simctl spawn booted launchctl list", undefined, true)
|
||||
endRunningApplication(appId: string): Q.Promise<string> {
|
||||
return tu.TestUtil.getProcessOutput("xcrun simctl spawn booted launchctl list", undefined)
|
||||
.then<string>(processListOutput => {
|
||||
// find the app's process
|
||||
var regex = new RegExp("(\\S+" + appId + "\\S+)");
|
||||
var execResult: any[] = regex.exec(processListOutput);
|
||||
if (execResult) {
|
||||
|
@ -151,68 +161,102 @@ export class IOSEmulatorManager implements IEmulatorManager {
|
|||
}
|
||||
})
|
||||
.then<string>(applicationLabel => {
|
||||
return tu.TestUtil.getProcessOutput("xcrun simctl spawn booted launchctl stop " + applicationLabel, undefined, true);
|
||||
})
|
||||
.then<void>(output => {
|
||||
console.log(output);
|
||||
// kill the app if we found the process
|
||||
return tu.TestUtil.getProcessOutput("xcrun simctl spawn booted launchctl stop " + applicationLabel, undefined);
|
||||
}, (error) => {
|
||||
// we couldn't find the app's process so it must not be running
|
||||
return Q.resolve(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts an already installed application by app id.
|
||||
*/
|
||||
restartApplication(appId: string): Q.Promise<void> {
|
||||
restartApplication(appId: string): Q.Promise<string> {
|
||||
return this.endRunningApplication(appId)
|
||||
.then<void>(() => {
|
||||
// wait for a second before restarting
|
||||
return Q.delay(1000);
|
||||
})
|
||||
.then(() => this.launchInstalledApplication(appId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates away from the current app, waits for a delay, then navigates to the specified app.
|
||||
* Navigates away from the current app, waits for a delay (defaults to 1 second), then navigates to the specified app.
|
||||
*/
|
||||
resumeApplication(appId: string, delayBeforeResumingMs: number): Q.Promise<void> {
|
||||
resumeApplication(appId: string, delayBeforeResumingMs: number = 1000): Q.Promise<string> {
|
||||
// open a default iOS app (for example, camera)
|
||||
return this.launchInstalledApplication("com.apple.camera")
|
||||
.then<void>(() => {
|
||||
console.log("Waiting for " + delayBeforeResumingMs + "ms before resuming the test application.");
|
||||
return Q.delay(delayBeforeResumingMs);
|
||||
})
|
||||
.then<void>(() => {
|
||||
.then<string>(() => {
|
||||
// reopen the app
|
||||
return this.launchInstalledApplication(appId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the emulator for a test.
|
||||
*/
|
||||
prepareEmulatorForTest(appId: string): Q.Promise<string> {
|
||||
return this.endRunningApplication(appId);
|
||||
}
|
||||
}
|
||||
|
||||
export class AndroidEmulatorManager implements IEmulatorManager {
|
||||
/**
|
||||
* Launches an already installed application by app id.
|
||||
*/
|
||||
public launchInstalledApplication(appId: string): Q.Promise<void> {
|
||||
return ProjectManager.ProjectManager.execAndLogChildProcess("adb shell monkey -p " + appId + " -c android.intent.category.LAUNCHER 1");
|
||||
launchInstalledApplication(appId: string): Q.Promise<string> {
|
||||
return ProjectManager.ProjectManager.execChildProcess("adb shell monkey -p " + appId + " -c android.intent.category.LAUNCHER 1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a running application given its app id.
|
||||
*/
|
||||
endRunningApplication(appId: string): Q.Promise<string> {
|
||||
return ProjectManager.ProjectManager.execChildProcess("adb shell am force-stop " + appId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts an already installed application by app id.
|
||||
*/
|
||||
restartApplication(appId: string): Q.Promise<void> {
|
||||
return ProjectManager.ProjectManager.runPlatform(tu.TestUtil.testRunDirectory, Android.getInstance(), true, tu.TestUtil.readTargetEmulator());
|
||||
restartApplication(appId: string): Q.Promise<string> {
|
||||
return this.endRunningApplication(appId)
|
||||
.then<void>(() => {
|
||||
// wait for a second before restarting
|
||||
return Q.delay(1000);
|
||||
})
|
||||
.then<string>(() => {
|
||||
return this.launchInstalledApplication(appId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates away from the current app, waits for a delay, then navigates to the specified app.
|
||||
* Navigates away from the current app, waits for a delay (defaults to 1 second), then navigates to the specified app.
|
||||
*/
|
||||
resumeApplication(appId: string, delayBeforeResumingMs: number): Q.Promise<void> {
|
||||
resumeApplication(appId: string, delayBeforeResumingMs: number = 1000): Q.Promise<string> {
|
||||
// open a default Android app (for example, settings)
|
||||
return this.launchInstalledApplication("com.android.settings")
|
||||
.then<void>(() => {
|
||||
console.log("Waiting for " + delayBeforeResumingMs + "ms before resuming the test application.");
|
||||
return Q.delay(delayBeforeResumingMs);
|
||||
})
|
||||
.then<void>(() => {
|
||||
.then<string>(() => {
|
||||
// reopen the app
|
||||
this.launchInstalledApplication(appId);
|
||||
return this.launchInstalledApplication(appId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the emulator for a test.
|
||||
*/
|
||||
prepareEmulatorForTest(appId: string): Q.Promise<string> {
|
||||
return this.endRunningApplication(appId)
|
||||
.then(() => { return ProjectManager.ProjectManager.execChildProcess("adb shell pm clear " + appId); });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@ import fs = require("fs");
|
|||
import mkdirp = require("mkdirp");
|
||||
|
||||
import platform = require("./platform");
|
||||
import tu = require("./testUtil");
|
||||
|
||||
var del = require("del");
|
||||
var archiver = require("archiver");
|
||||
|
@ -22,7 +23,7 @@ var archiver = require("archiver");
|
|||
* In charge of Cordova project related operations.
|
||||
*/
|
||||
export class ProjectManager {
|
||||
public static ANDROID_KEY_PLACEHOLDER: string = "CODE_PUSH_ANDROID_DEPOYMENT_KEY";
|
||||
public static ANDROID_KEY_PLACEHOLDER: string = "CODE_PUSH_ANDROID_DEPLOYMENT_KEY";
|
||||
public static IOS_KEY_PLACEHOLDER: string = "CODE_PUSH_IOS_DEPLOYMENT_KEY";
|
||||
public static SERVER_URL_PLACEHOLDER: string = "CODE_PUSH_SERVER_URL";
|
||||
public static INDEX_JS_PLACEHOLDER: string = "CODE_PUSH_INDEX_JS_PATH";
|
||||
|
@ -35,61 +36,82 @@ export class ProjectManager {
|
|||
* Creates a new cordova test application at the specified path, and configures it
|
||||
* with the given server URL, android and ios deployment keys.
|
||||
*/
|
||||
public static setupTemplate(destinationPath: string,
|
||||
public static setupTemplate(projectDirectory: string,
|
||||
templatePath: string,
|
||||
serverURL: string,
|
||||
androidKey: string,
|
||||
iosKey: string,
|
||||
appName: string,
|
||||
appNamespace: string,
|
||||
jsPath: string,
|
||||
appVersion: string = ProjectManager.DEFAULT_APP_VERSION): Q.Promise<void> {
|
||||
var configXmlPath = path.join(destinationPath, "config.xml");
|
||||
var indexHtmlPath = path.join(destinationPath, "www/index.html");
|
||||
var indexJsPath = path.join(destinationPath, "www/" + jsPath);
|
||||
version: string = ProjectManager.DEFAULT_APP_VERSION): Q.Promise<string> {
|
||||
var configXmlPath = path.join(projectDirectory, "config.xml");
|
||||
var indexHtmlPath = path.join(projectDirectory, "www/index.html");
|
||||
var setupScenario = "js/scenarioSetup.js";
|
||||
var setupScenarioPath = path.join(projectDirectory, "www/", setupScenario);
|
||||
|
||||
if (fs.existsSync(destinationPath)) {
|
||||
del.sync([destinationPath], { force: true });
|
||||
if (fs.existsSync(projectDirectory)) {
|
||||
del.sync([projectDirectory], { force: true });
|
||||
}
|
||||
mkdirp.sync(destinationPath);
|
||||
mkdirp.sync(projectDirectory);
|
||||
|
||||
return ProjectManager.execAndLogChildProcess("cordova create " + destinationPath + " " + appNamespace + " " + appName + " --copy-from " + templatePath)
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, configXmlPath, ProjectManager.ANDROID_KEY_PLACEHOLDER, androidKey))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, configXmlPath, ProjectManager.IOS_KEY_PLACEHOLDER, iosKey))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, configXmlPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.INDEX_JS_PLACEHOLDER, jsPath))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.CODE_PUSH_APP_VERSION_PLACEHOLDER, appVersion))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, indexJsPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL));
|
||||
return ProjectManager.execChildProcess("cordova create " + projectDirectory + " " + appNamespace + " " + appName + " --copy-from " + templatePath)
|
||||
// copy the correct values into the config.xml file
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, configXmlPath, ProjectManager.ANDROID_KEY_PLACEHOLDER, androidKey))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, configXmlPath, ProjectManager.IOS_KEY_PLACEHOLDER, iosKey))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, configXmlPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
// copy the correct values into the index.html file
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.CODE_PUSH_APP_VERSION_PLACEHOLDER, version))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.CODE_PUSH_APP_VERSION_PLACEHOLDER, version))
|
||||
// use the setup scenario
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, setupScenarioPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.INDEX_JS_PLACEHOLDER, setupScenario))
|
||||
.then<string>(ProjectManager.replaceString.bind(undefined, indexHtmlPath, ProjectManager.CODE_PUSH_APP_VERSION_PLACEHOLDER, version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an already existing Cordova project.
|
||||
* Sets up the scenario for a test in an already existing Cordova project.
|
||||
*/
|
||||
public static updateProject(destinationPath: string, templatePath: string, version: string, platform: string, jsPath: string, serverURL: string): Q.Promise<void> {
|
||||
var templateIndexPath = path.join(templatePath, "www/index.html");
|
||||
var destination = path.join(destinationPath, "www/index.html");
|
||||
public static setupScenario(projectDirectory: string, templatePath: string, jsPath: string, serverURL: string, build: boolean = true, version: string = ProjectManager.DEFAULT_APP_VERSION): Q.Promise<string> {
|
||||
var indexHtml = "www/index.html";
|
||||
var templateIndexPath = path.join(templatePath, indexHtml);
|
||||
var destinationIndexPath = path.join(projectDirectory, indexHtml);
|
||||
|
||||
var scenarioJs = "www/" + jsPath;
|
||||
var templateScenarioJsPath = path.join(templatePath, scenarioJs);
|
||||
var destinationScenarioJsPath = path.join(projectDirectory, scenarioJs);
|
||||
|
||||
return ProjectManager.copyFile(templateIndexPath, destinationPath, true)
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destination, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destination, ProjectManager.INDEX_JS_PLACEHOLDER, jsPath))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destination, ProjectManager.CODE_PUSH_APP_VERSION_PLACEHOLDER, version));
|
||||
return ProjectManager.copyFile(templateIndexPath, destinationIndexPath, true)
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destinationIndexPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destinationIndexPath, ProjectManager.INDEX_JS_PLACEHOLDER, jsPath))
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destinationIndexPath, ProjectManager.CODE_PUSH_APP_VERSION_PLACEHOLDER, version))
|
||||
.then<void>(() => {
|
||||
return ProjectManager.copyFile(templateScenarioJsPath, destinationScenarioJsPath, true);
|
||||
})
|
||||
.then<void>(ProjectManager.replaceString.bind(undefined, destinationScenarioJsPath, ProjectManager.SERVER_URL_PLACEHOLDER, serverURL))
|
||||
.then<string>(() => {
|
||||
return build ? ProjectManager.buildPlatform(projectDirectory) : ProjectManager.preparePlatform(projectDirectory);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CodePush update package zip for a Cordova project.
|
||||
*/
|
||||
public static createUpdateArchive(projectLocation: string, targetPlatform: platform.IPlatform, isDiff?: boolean): Q.Promise<string> {
|
||||
public static createUpdateArchive(projectDirectory: string, isDiff?: boolean): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
|
||||
var deferred = Q.defer<string>();
|
||||
var archive = archiver.create("zip", {});
|
||||
var archivePath = path.join(projectLocation, "update.zip");
|
||||
var archivePath = path.join(projectDirectory, "update.zip");
|
||||
console.log("Creating a project update archive at: " + archivePath);
|
||||
|
||||
if (fs.existsSync(archivePath)) {
|
||||
fs.unlinkSync(archivePath);
|
||||
}
|
||||
var writeStream = fs.createWriteStream(archivePath);
|
||||
var targetFolder = targetPlatform.getPlatformWwwPath(projectLocation);
|
||||
var targetFolder = targetPlatform.getPlatformWwwPath(projectDirectory);
|
||||
|
||||
writeStream.on("close", function() {
|
||||
deferred.resolve(archivePath);
|
||||
|
@ -113,38 +135,69 @@ export class ProjectManager {
|
|||
/**
|
||||
* Adds a plugin to a Cordova project.
|
||||
*/
|
||||
public static addPlugin(projectFolder: string, plugin: string): Q.Promise<void> {
|
||||
return ProjectManager.execAndLogChildProcess("cordova plugin add " + plugin, { cwd: projectFolder });
|
||||
public static addPlugin(projectFolder: string, plugin: string): Q.Promise<string> {
|
||||
return ProjectManager.execChildProcess("cordova plugin add " + plugin, { cwd: projectFolder });
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a specific platform of a Cordova project.
|
||||
*/
|
||||
public static buildPlatform(projectFolder: string, targetPlatform: platform.IPlatform): Q.Promise<void> {
|
||||
return ProjectManager.execAndLogChildProcess("cordova build " + targetPlatform.getCordovaName(), { cwd: projectFolder });
|
||||
public static buildPlatform(projectFolder: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
|
||||
// don't log the iOS build output because it is too verbose and overflows the buffer
|
||||
return ProjectManager.execChildProcess("cordova build " + targetPlatform.getCordovaName(), { cwd: projectFolder }, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a specific platform of a Cordova project.
|
||||
*/
|
||||
public static preparePlatform(projectFolder: string, targetPlatform: platform.IPlatform): Q.Promise<void> {
|
||||
return ProjectManager.execAndLogChildProcess("cordova prepare " + targetPlatform.getCordovaName(), { cwd: projectFolder });
|
||||
public static preparePlatform(projectFolder: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
|
||||
return ProjectManager.execChildProcess("cordova prepare " + targetPlatform.getCordovaName(), { cwd: projectFolder });
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a project to a given target / platform.
|
||||
* Launch the test app on the given target / platform.
|
||||
*/
|
||||
public static runPlatform(projectFolder: string, targetPlatform: platform.IPlatform, skipBuild: boolean = false, target?: string): Q.Promise<void> {
|
||||
public static launchApplication(appNamespace: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
return targetPlatform.getEmulatorManager().launchInstalledApplication(appNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the test app on the given target / platform.
|
||||
*/
|
||||
public static endRunningApplication(appNamespace: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
return targetPlatform.getEmulatorManager().endRunningApplication(appNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the emulator for a test.
|
||||
*/
|
||||
public static prepareEmulatorForTest(appNamespace: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
return targetPlatform.getEmulatorManager().prepareEmulatorForTest(appNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test app on the given target / platform.
|
||||
*/
|
||||
public static runPlatform(projectFolder: string, skipBuild: boolean = true, target?: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
var runTarget = target ? " --target " + target : "";
|
||||
var nobuild = skipBuild ? " --nobuild" : "";
|
||||
return ProjectManager.execAndLogChildProcess("cordova run " + targetPlatform.getCordovaName() + runTarget + nobuild, { cwd: projectFolder });
|
||||
return ProjectManager.execChildProcess("cordova run " + targetPlatform.getCordovaName() + runTarget + nobuild, { cwd: projectFolder });
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a platform to a Cordova project.
|
||||
*/
|
||||
public static addPlatform(projectFolder: string, targetPlatform: platform.IPlatform, version?: string): Q.Promise<void> {
|
||||
return ProjectManager.execAndLogChildProcess("cordova platform add " + targetPlatform.getCordovaName() + (version ? "@" + version : ""), { cwd: projectFolder });
|
||||
public static addPlatform(projectFolder: string, version?: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
return ProjectManager.execChildProcess("cordova platform add " + targetPlatform.getCordovaName() + (version ? "@" + version : ""), { cwd: projectFolder });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,34 +210,24 @@ export class ProjectManager {
|
|||
/**
|
||||
* Stops and restarts an application specified by its namespace identifier.
|
||||
*/
|
||||
public static restartApplication(targetPlatform: platform.IPlatform, namespace: string, testRunDirectory: string, targetEmulator: string): Q.Promise<void> {
|
||||
var emulatorManager = targetPlatform.getEmulatorManager();
|
||||
if (emulatorManager) {
|
||||
return emulatorManager.restartApplication(namespace);
|
||||
} else {
|
||||
console.log("No emulator manager found!");
|
||||
return null;
|
||||
}
|
||||
public static restartApplication(appNamespace: string): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
return targetPlatform.getEmulatorManager().restartApplication(appNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates away from the application and then navigates back to it.
|
||||
*/
|
||||
public static resumeApplication(delayBeforeResumingMs: number, targetPlatform: platform.IPlatform, namespace: string, testRunDirectory: string, targetEmulator: string): Q.Promise<void> {
|
||||
var emulatorManager = targetPlatform.getEmulatorManager();
|
||||
if (emulatorManager) {
|
||||
return emulatorManager.resumeApplication(namespace, delayBeforeResumingMs);
|
||||
} else {
|
||||
console.log("No emulator manager found!");
|
||||
return null;
|
||||
}
|
||||
public static resumeApplication(namespace: string, delayBeforeResumingMs: number = 1000): Q.Promise<string> {
|
||||
var targetPlatform = platform.PlatformResolver.resolvePlatform(tu.TestUtil.readTargetPlatform());
|
||||
return targetPlatform.getEmulatorManager().resumeApplication(namespace, delayBeforeResumingMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a child process and logs its output to the console.
|
||||
* Executes a child process and logs its output to the console and returns its output in the promise as a string
|
||||
*/
|
||||
public static execAndLogChildProcess(command: string, options?: child_process.IExecOptions): Q.Promise<void> {
|
||||
var deferred = Q.defer<void>();
|
||||
public static execChildProcess(command: string, options?: child_process.IExecOptions, logOutput: boolean = true): Q.Promise<string> {
|
||||
var deferred = Q.defer<string>();
|
||||
|
||||
options = options || {};
|
||||
options.maxBuffer = 1024 * 500;
|
||||
|
@ -192,14 +235,14 @@ export class ProjectManager {
|
|||
console.log("Running command: " + command);
|
||||
child_process.exec(command, options, (error: Error, stdout: Buffer, stderr: Buffer) => {
|
||||
|
||||
stdout && console.log(stdout);
|
||||
if (logOutput) stdout && console.log(stdout);
|
||||
stderr && console.error(stderr);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
deferred.reject(error);
|
||||
} else {
|
||||
deferred.resolve(undefined);
|
||||
deferred.resolve(stdout.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<platform name="android">
|
||||
<allow-intent href="market:*" />
|
||||
<!-- CodePush deployment key for the Android platform. -->
|
||||
<preference name="CodePushDeploymentKey" value="CODE_PUSH_ANDROID_DEPOYMENT_KEY" />
|
||||
<preference name="CodePushDeploymentKey" value="CODE_PUSH_ANDROID_DEPLOYMENT_KEY" />
|
||||
</platform>
|
||||
|
||||
<platform name="ios">
|
||||
|
|
|
@ -37,12 +37,12 @@ var app = {
|
|||
function () {
|
||||
/* success */
|
||||
app.sendTestMessage("RESTART_SUCCEEDED");
|
||||
callback();
|
||||
callback && callback();
|
||||
},
|
||||
function () {
|
||||
/* error */
|
||||
app.sendTestMessage("RESTART_FAILED");
|
||||
callback();
|
||||
callback && callback();
|
||||
});
|
||||
},
|
||||
/* sends the current and pending package to the mock server */
|
||||
|
@ -53,7 +53,7 @@ var app = {
|
|||
window.codePush.getCurrentPackage(function (currentPackage) {
|
||||
console.log("Current package: " + currentPackage);
|
||||
app.sendTestMessage("CURRENT_PACKAGE", [currentPackage ? currentPackage.packageHash : null]);
|
||||
callback();
|
||||
callback && callback();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function () {
|
||||
this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
onDeviceReady: function () {
|
||||
app.receivedDeviceReady();
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - setup)";
|
||||
console.log('Received Event: deviceready');
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
|
@ -0,0 +1,45 @@
|
|||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function () {
|
||||
this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
onDeviceReady: function () {
|
||||
app.receivedDeviceReady();
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync mandatory)";
|
||||
console.log('Received Event: deviceready');
|
||||
/* invoke sync with no UI options */
|
||||
window.codePush.sync(
|
||||
function (status) {
|
||||
switch(status) {
|
||||
case SyncStatus.UP_TO_DATE:
|
||||
case SyncStatus.UPDATE_INSTALLED:
|
||||
case SyncStatus.UPDATE_IGNORED:
|
||||
case SyncStatus.ERROR:
|
||||
case SyncStatus.IN_PROGRESS:
|
||||
app.sendTestMessage("SYNC_STATUS", [status]);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
installMode: InstallMode.ON_NEXT_RESTART
|
||||
});
|
||||
},
|
||||
sendTestMessage: function (message, args) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false);
|
||||
var body = JSON.stringify({ message: message, args: args });
|
||||
console.log("Sending test message body: " + body);
|
||||
|
||||
xhr.setRequestHeader("Content-type", "application/json");
|
||||
xhr.send(body);
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
|
@ -0,0 +1,46 @@
|
|||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function () {
|
||||
this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
onDeviceReady: function () {
|
||||
app.receivedDeviceReady();
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync mandatory)";
|
||||
console.log('Received Event: deviceready');
|
||||
/* invoke sync with no UI options */
|
||||
window.codePush.sync(
|
||||
function (status) {
|
||||
switch(status) {
|
||||
case SyncStatus.UP_TO_DATE:
|
||||
case SyncStatus.UPDATE_INSTALLED:
|
||||
case SyncStatus.UPDATE_IGNORED:
|
||||
case SyncStatus.ERROR:
|
||||
case SyncStatus.IN_PROGRESS:
|
||||
app.sendTestMessage("SYNC_STATUS", [status]);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
installMode: InstallMode.IMMEDIATE,
|
||||
mandatoryInstallMode: InstallMode.ON_NEXT_RESTART
|
||||
});
|
||||
},
|
||||
sendTestMessage: function (message, args) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false);
|
||||
var body = JSON.stringify({ message: message, args: args });
|
||||
console.log("Sending test message body: " + body);
|
||||
|
||||
xhr.setRequestHeader("Content-type", "application/json");
|
||||
xhr.send(body);
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
|
@ -0,0 +1,50 @@
|
|||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function () {
|
||||
this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
document.addEventListener('resume', this.onResume, false);
|
||||
},
|
||||
onDeviceReady: function () {
|
||||
app.receivedDeviceReady();
|
||||
},
|
||||
onResume: function () {
|
||||
app.sendTestMessage("APPLICATION_RESUMED");
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync mandatory)";
|
||||
console.log('Received Event: deviceready');
|
||||
/* invoke sync with no UI options */
|
||||
window.codePush.sync(
|
||||
function (status) {
|
||||
switch(status) {
|
||||
case SyncStatus.UP_TO_DATE:
|
||||
case SyncStatus.UPDATE_INSTALLED:
|
||||
case SyncStatus.UPDATE_IGNORED:
|
||||
case SyncStatus.ERROR:
|
||||
case SyncStatus.IN_PROGRESS:
|
||||
app.sendTestMessage("SYNC_STATUS", [status]);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
installMode: InstallMode.ON_NEXT_RESTART,
|
||||
mandatoryInstallMode: InstallMode.ON_NEXT_RESUME
|
||||
});
|
||||
},
|
||||
sendTestMessage: function (message, args) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "CODE_PUSH_SERVER_URL/reportTestMessage", false);
|
||||
var body = JSON.stringify({ message: message, args: args });
|
||||
console.log("Sending test message body: " + body);
|
||||
|
||||
xhr.setRequestHeader("Content-type", "application/json");
|
||||
xhr.send(body);
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
|
@ -15,7 +15,7 @@ var app = {
|
|||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on restart minbackdur = 30s)";
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on restart with minbackdur)";
|
||||
console.log('Received Event: deviceready');
|
||||
/* invoke sync with no UI options */
|
||||
window.codePush.sync(
|
||||
|
|
|
@ -15,7 +15,7 @@ var app = {
|
|||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on resume minbackdur = 0s)";
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on resume with no minbackdur)";
|
||||
console.log('Received Event: deviceready');
|
||||
/* invoke sync with no UI options */
|
||||
window.codePush.sync(
|
||||
|
|
|
@ -15,7 +15,7 @@ var app = {
|
|||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on resume minbackdur = 30s)";
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - sync on resume with minbackdur)";
|
||||
console.log('Received Event: deviceready');
|
||||
/* invoke sync with no UI options */
|
||||
window.codePush.sync(
|
||||
|
@ -34,7 +34,7 @@ var app = {
|
|||
},
|
||||
{
|
||||
installMode: InstallMode.ON_NEXT_RESUME,
|
||||
minimumBackgroundDuration: 15
|
||||
minimumBackgroundDuration: 10
|
||||
});
|
||||
},
|
||||
sendTestMessage: function (message, args) {
|
||||
|
|
|
@ -11,7 +11,7 @@ var app = {
|
|||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedDeviceReady: function () {
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - update sync)";
|
||||
document.getElementById("deviceready").innerText = "Device is ready (scenario - update sync 2x)";
|
||||
console.log('Received Event: deviceready');
|
||||
app.sendTestMessage("DEVICE_READY_AFTER_UPDATE");
|
||||
/* invoke sync with UI options such that the update will not be installed */
|
||||
|
|
530
test/test.ts
530
test/test.ts
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -299,7 +299,7 @@ declare enum InstallMode {
|
|||
*/
|
||||
interface InstallOptions {
|
||||
/**
|
||||
* Used to specity the InstallMode used for the install operation. This is optional and defaults to InstallMode.ON_NEXT_RESTART.
|
||||
* Used to specify the InstallMode used for the install operation. This is optional and defaults to InstallMode.ON_NEXT_RESTART.
|
||||
*/
|
||||
installMode?: InstallMode;
|
||||
|
||||
|
@ -307,6 +307,11 @@ interface InstallOptions {
|
|||
* If installMode === ON_NEXT_RESUME, the minimum amount of time (in seconds) which needs to pass with the app in the background before an update install occurs when the app is resumed.
|
||||
*/
|
||||
minimumBackgroundDuration?: number;
|
||||
|
||||
/**
|
||||
* Used to specify the InstallMode used for the install operation if the update is mandatory. This is optional and defaults to InstallMode.IMMEDIATE.
|
||||
*/
|
||||
mandatoryInstallMode?: InstallMode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -386,6 +386,7 @@ class CodePush implements CodePushCordovaPlugin {
|
|||
ignoreFailedUpdates: true,
|
||||
installMode: InstallMode.ON_NEXT_RESTART,
|
||||
minimumBackgroundDuration: 0,
|
||||
mandatoryInstallMode: InstallMode.IMMEDIATE,
|
||||
updateDialog: false,
|
||||
deploymentKey: undefined
|
||||
};
|
||||
|
|
|
@ -110,20 +110,23 @@ class LocalPackage extends Package implements ILocalPackage {
|
|||
LocalPackage.getCurrentOrDefaultPackage((oldPackage: LocalPackage) => {
|
||||
LocalPackage.backupPackageInformationFile((backupError: Error) => {
|
||||
backupError && CodePushUtil.logMessage("First update: back up package information skipped. ");
|
||||
/* continue on error, current package information is missing if this is the fist update */
|
||||
/* continue on error, current package information is missing if this is the first update */
|
||||
this.writeNewPackageMetadata(deployDir, (writeMetadataError: Error) => {
|
||||
if (writeMetadataError) {
|
||||
installError && installError(writeMetadataError);
|
||||
} else {
|
||||
var invokeSuccessAndInstall = () => {
|
||||
CodePushUtil.logMessage("Install succeeded.");
|
||||
if (installOptions.installMode === InstallMode.IMMEDIATE) {
|
||||
var installModeToUse: InstallMode = this.isMandatory ? installOptions.mandatoryInstallMode : installOptions.installMode;
|
||||
if (installModeToUse === InstallMode.IMMEDIATE) {
|
||||
/* invoke success before navigating */
|
||||
installSuccess && installSuccess();
|
||||
/* no neeed for callbacks, the javascript context will reload */
|
||||
cordova.exec(() => { }, () => { }, "CodePush", "install", [deployDir.fullPath, installOptions.installMode.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
/* no need for callbacks, the javascript context will reload */
|
||||
cordova.exec(() => { }, () => { }, "CodePush", "install", [deployDir.fullPath,
|
||||
installModeToUse.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
} else {
|
||||
cordova.exec(() => { installSuccess && installSuccess(); }, () => { installError && installError(); }, "CodePush", "install", [deployDir.fullPath, installOptions.installMode.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
cordova.exec(() => { installSuccess && installSuccess(); }, () => { installError && installError(); }, "CodePush", "install", [deployDir.fullPath,
|
||||
installModeToUse.toString(), installOptions.minimumBackgroundDuration.toString()]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -444,7 +447,8 @@ class LocalPackage extends Package implements ILocalPackage {
|
|||
if (!LocalPackage.DefaultInstallOptions) {
|
||||
LocalPackage.DefaultInstallOptions = {
|
||||
installMode: InstallMode.ON_NEXT_RESTART,
|
||||
minimumBackgroundDuration: 0
|
||||
minimumBackgroundDuration: 0,
|
||||
mandatoryInstallMode: InstallMode.IMMEDIATE
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче