diff --git a/.gitignore b/.gitignore index 257665b29..c4fc49f87 100644 --- a/.gitignore +++ b/.gitignore @@ -117,9 +117,6 @@ proguard/ # Android Studio captures folder captures/ -# Remove after this framework is published on NPM -code-push-plugin-testing-framework/node_modules - # Windows windows/.vs/ windows/obj/ @@ -169,3 +166,6 @@ cli/dist/ !bin/ bin/* !bin/code-push.js + +# E2E mock server data +e2e/mock-server/data/ diff --git a/Examples/create-app.js b/Examples/create-app.js deleted file mode 100644 index 50a15602c..000000000 --- a/Examples/create-app.js +++ /dev/null @@ -1,353 +0,0 @@ -/* -The script serves to generate CodePushified React Native app to reproduce issues or for testing purposes. - -NOTE: Use CodePushDemoApp and CodePushDemoAppCpp as reference how to implement CodePush in your app. For creating a working demo app -follow steps bellow. - -Requirements: - 1. npm i -g react-native-cli - 2. npm i -g appcenter-cli - 3. appcenter login - 4. If you use this script on macOS for react-native v0.60+ then you need to have CocoaPods installed. Use this command to (re)install CocoaPods: - sudo gem install cocoapods -n /usr/local/bin - -Usage: node create-app.js - 1. node create-app.js - 2. node create-app.js myapp - 3. node create-app.js myapp react-native@0.62 react-native-code-push@6.1.0 - 4. node create-app.js myapp react-native@latest Microsoft/react-native-code-push - -Parameters: - 1. - CodePushDemoAppTest - 2. - react-native@latest - 3. - react-native-code-push@latest -*/ - -const fs = require('fs'); -const path = require('path'); -const nexpect = require('./nexpect'); -const child_process = require('child_process'); -const execSync = child_process.execSync; - -const args = process.argv.slice(2); -const appName = args[0] || 'CodePushDemoAppTest'; - -if (fs.existsSync(appName)) { - console.error(`Folder with name "${appName}" already exists! Please delete`); - process.exit(); -} - - -const appNameAndroid = `${appName}-android`; -const appNameIOS = `${appName}-ios`; -let owner = null; -const reactNativeVersion = args[1] || `react-native@${execCommand('npm view react-native version')}`.trim(); -const reactNativeVersionIsLowerThanV049 = isReactNativeVersionLowerThan(49); -const reactNativeCodePushVersion = args[2] || `react-native-code-push@${execCommand('npm view react-native-code-push version')}`.trim(); - -const reactNativeVersionIsLowerThanV073 = isReactNativeVersionLowerThan(73) -if (!isReactNativeVersionLowerThan(60) && process.platform === "darwin") { - try { - console.log("Verify that CocoaPods installed"); - execCommand("pod --version"); - console.log("CocoaPods has installed"); - } catch { - console.error(`'CocoaPods' are required to run the script, you can install it with\n'sudo gem install cocoapods -n /usr/local/bin'\ncommand`); - process.exit(); - } -} - -console.log(`App name: ${appName}`); -console.log(`React Native version: ${reactNativeVersion}`); -console.log(`React Native Module for CodePush version: ${reactNativeCodePushVersion} \n`); - -let androidStagingDeploymentKey = null; -let iosStagingDeploymentKey = null; - - - -//GENERATE START -createCodePushApp(appNameAndroid, 'Android'); -createCodePushApp(appNameIOS, 'iOS'); - -generatePlainReactNativeApp(appName, reactNativeVersion); -process.chdir(appName); -installCodePush(reactNativeCodePushVersion); -linkCodePush(androidStagingDeploymentKey, iosStagingDeploymentKey); -//GENERATE END - - - -function createCodePushApp(name, os) { - try { - console.log(`Creating CodePush app "${name}" to release updates for ${os}...`); - try { - const appResult = execCommand(`appcenter apps create -d ${name} -n ${name} -o ${os} -p React-Native --output json`); - const app = JSON.parse(appResult); - owner = app.owner.name; - console.log(`App "${name}" has been created \n`); - } catch (e) { - console.error(`Error: Unable to create CodePush app. Please check that you haven't application with "${name}" name on portal.`,); - console.error("Error: ", e.toString()); - } - execCommand(`appcenter codepush deployment add -a ${owner}/${name} Staging`); - } catch (e) { - console.error("Error", e.toString()); - } - - try { - const deploymentKeysResult = execCommand(`appcenter codepush deployment list -a ${owner}/${name} -k --output json`); - const deploymentKeys = JSON.parse(deploymentKeysResult); - const stagingDeploymentKey = deploymentKeys[0][1]; - console.log(`Deployment key for ${os}: ${stagingDeploymentKey}`); - console.log(`Use "appcenter codepush release-react ${owner}/${name}" command to release updates for ${os} \n`); - - switch (os) { - case 'Android': - androidStagingDeploymentKey = stagingDeploymentKey; - break; - case 'iOS': - iosStagingDeploymentKey = stagingDeploymentKey; - break; - } - } catch (e) { - console.error("Error: Unable to load deployment keys"); - console.error("Error: ", e.toString()); - } - -} - -function generatePlainReactNativeApp(appName, reactNativeVersion) { - console.log(`Installing React Native...`); - execCommand(`react-native init ${appName} --version ${reactNativeVersion.split('@')[1]}`); - console.log(`React Native has been installed \n`); -} - -function installCodePush(reactNativeCodePushVersion) { - console.log(`Installing React Native Module for CodePush...`); - execCommand(`npm i ${reactNativeCodePushVersion}`); - console.log(`React Native Module for CodePush has been installed \n`); -} - -function linkCodePush(androidStagingDeploymentKey, iosStagingDeploymentKey) { - console.log(`Linking React Native Module for CodePush...`); - if (isReactNativeVersionLowerThan(60)) { - nexpect.spawn(`react-native link react-native-code-push`) - .wait("What is your CodePush deployment key for Android (hit to ignore)") - .sendline(androidStagingDeploymentKey) - .wait("What is your CodePush deployment key for iOS (hit to ignore)") - .sendline(iosStagingDeploymentKey) - .run(function (err) { - if (!err) { - console.log(`React Native Module for CodePush has been linked \n`); - setupAssets(); - } - else { - console.log(err); - } - }); - } else { - androidSetup(); - if (process.platform === 'darwin') { - iosSetup(); - } else { - console.log('Your OS is not "Mac OS" so the iOS application will not be configured') - } - setupAssets(); - console.log(`React Native Module for CodePush has been linked \n`); - } -} - -function setupAssets() { - let fileToEdit; - if (reactNativeVersionIsLowerThanV049) { - fs.unlinkSync('./index.ios.js'); - fs.unlinkSync('./index.android.js'); - - fs.writeFileSync('demo.js', fs.readFileSync('../CodePushDemoApp-pre0.49/demo.js')); - fs.writeFileSync('index.ios.js', fs.readFileSync('../CodePushDemoApp-pre0.49/index.ios.js')); - fs.writeFileSync('index.android.js', fs.readFileSync('../CodePushDemoApp-pre0.49/index.android.js')); - fileToEdit = 'demo.js' - } else { - fs.writeFileSync('index.js', fs.readFileSync(__dirname + '/CodePushDemoApp/index.js')); - fs.writeFileSync('App.js', fs.readFileSync(__dirname + '/CodePushDemoApp/App.js')); - fileToEdit = 'index.js' - } - - copyRecursiveSync(__dirname + '/CodePushDemoApp/images', './images'); - - fs.readFile(fileToEdit, 'utf8', function (err, data) { - if (err) { - return console.error(err); - } - const result = data.replace(/CodePushDemoApp/g, appName); - - fs.writeFile(fileToEdit, result, 'utf8', function (err) { - if (err) return console.error(err); - - if (!/^win/.test(process.platform)) { - optimizeToTestInDebugMode(); - process.chdir('../'); - grantAccess(appName); - } - console.log(`\nReact Native app "${appName}" has been generated and CodePushified!`); - process.exit(); - }); - }); -} - -function optimizeToTestInDebugMode() { - let rnXcodeShLocationFolder = 'scripts'; - try { - const rnVersions = JSON.parse(execCommand(`npm view react-native versions --json`)); - const currentRNversion = JSON.parse(fs.readFileSync('./package.json'))['dependencies']['react-native']; - if (rnVersions.indexOf(currentRNversion) > -1 && - rnVersions.indexOf(currentRNversion) < rnVersions.indexOf("0.46.0-rc.0")) { - rnXcodeShLocationFolder = 'packager'; - } - } catch (e) { } - - const rnXcodeShPath = `node_modules/react-native/${rnXcodeShLocationFolder}/react-native-xcode.sh`; - // Replace "if [[ "$PLATFORM_NAME" == *simulator ]]; then" with "if false; then" to force bundling - execCommand(`sed -ie 's/if \\[\\[ "\$PLATFORM_NAME" == \\*simulator \\]\\]; then/if false; then/' ${rnXcodeShPath}`); - execCommand(`perl -i -p0e 's/#ifdef DEBUG.*?#endif/jsCodeLocation = [CodePush bundleURL];/s' ios/${appName}/${getAppDelegateName()}`); - reactNativeVersionIsLowerThanV073 && execCommand(`sed -ie 's/targetName.toLowerCase().contains("release")/true/' node_modules/react-native/react.gradle`); -} - -function grantAccess(folderPath) { - execCommand('chown -R `whoami` ' + folderPath); -} - -function copyRecursiveSync(src, dest) { - const exists = fs.existsSync(src); - const stats = exists && fs.statSync(src); - const isDirectory = exists && stats.isDirectory(); - if (exists && isDirectory) { - fs.mkdirSync(dest); - fs.readdirSync(src).forEach(function (childItemName) { - copyRecursiveSync(path.join(src, childItemName), - path.join(dest, childItemName)); - }); - } else { - fs.linkSync(src, dest); - } -} - -function isReactNativeVersionLowerThan(version) { - if (!reactNativeVersion || - reactNativeVersion == "react-native@latest" || - reactNativeVersion == "react-native@next") - return false; - - const reactNativeVersionNumberString = reactNativeVersion.split("@")[1]; - return reactNativeVersionNumberString.split('.')[1] < version; -} - -// Configuring android applications for react-native version higher than 0.60 -function androidSetup() { - const buildGradlePath = path.join('android', 'app', 'build.gradle'); - const settingsGradlePath = path.join('android', 'settings.gradle'); - const mainApplicationType = reactNativeVersionIsLowerThanV073 ? 'java' : 'kt'; - const mainApplicationPath = path.join('android', 'app', 'src', 'main', 'java', 'com', appName, `MainApplication.${mainApplicationType}`); - const stringsResourcesPath = path.join('android', 'app', 'src', 'main', 'res', 'values', 'strings.xml'); - - let stringsResourcesContent = fs.readFileSync(stringsResourcesPath, "utf8"); - const insertAfterString = ""; - const deploymentKeyString = `\t${androidStagingDeploymentKey || "deployment-key-here"}`; - stringsResourcesContent = stringsResourcesContent.replace(insertAfterString, `${insertAfterString}\n${deploymentKeyString}`); - fs.writeFileSync(stringsResourcesPath, stringsResourcesContent); - - let buildGradleContents = fs.readFileSync(buildGradlePath, "utf8"); - const reactGradleLink = buildGradleContents.match(/\napply from: ["'].*?react\.gradle["']/); - const codePushGradleLink = `\napply from: "../../node_modules/react-native-code-push/android/codepush.gradle"`; - if (reactGradleLink != null) { - buildGradleContents = buildGradleContents.replace(reactGradleLink[0], - `${reactGradleLink[0]}${codePushGradleLink}`); - fs.writeFileSync(buildGradlePath, buildGradleContents); - } - // react.gradle script removed from 0.71 thus this workaround - else { - const appPluginLastLine = buildGradleContents.match(/apply plugin: "com.facebook.react"/)[0]; - buildGradleContents = buildGradleContents.replace(appPluginLastLine, `${appPluginLastLine}${codePushGradleLink}`) - fs.writeFileSync(buildGradlePath, buildGradleContents); - } - - let settingsGradleContents = fs.readFileSync(settingsGradlePath, "utf8"); - const settingsGradleInclude = "include \':app\'"; - const codePushProjectImport = `':react-native-code-push'\nproject(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')`; - settingsGradleContents = settingsGradleContents.replace(settingsGradleInclude, - `${settingsGradleInclude}, ${codePushProjectImport}`); - fs.writeFileSync(settingsGradlePath, settingsGradleContents); - - let importCodePush = `\nimport com.microsoft.codepush.react.CodePush;`; - let reactNativeHostInstantiationImport = "import android.app.Application;"; - let mainApplicationContents = fs.readFileSync(mainApplicationPath, "utf8"); - let getJSBundleFileOverride = ""; - let reactNativeHostInstantiation = ""; - - // handle react-native version with java - if (reactNativeVersionIsLowerThanV073) { - reactNativeHostInstantiation = "new ReactNativeHost(this) {"; - - getJSBundleFileOverride = ` - @Override - protected String getJSBundleFile(){ - return CodePush.getJSBundleFile(); - } - `; - } - // handle react-native version with kotlin - else { - reactNativeHostInstantiation = "object : DefaultReactNativeHost(this) {" - getJSBundleFileOverride = ` - override fun getJSBundleFile(): String { - return CodePush.getJSBundleFile() - } - `; - importCodePush = importCodePush.replace(';', ''); - reactNativeHostInstantiationImport = reactNativeHostInstantiationImport.replace(';', ''); - } - mainApplicationContents = mainApplicationContents.replace(reactNativeHostInstantiation, - `${reactNativeHostInstantiation}${getJSBundleFileOverride}`); - - mainApplicationContents = mainApplicationContents.replace(reactNativeHostInstantiationImport, - `${reactNativeHostInstantiationImport}${importCodePush}`); - fs.writeFileSync(mainApplicationPath, mainApplicationContents); -} - -function getAppDelegateName() { return fs.readdirSync(path.join('ios', appName)).find(file => file === ('AppDelegate.mm') || file === 'AppDelegate.m') }; - -// Configuring ios applications for react-native version higher than 0.60 -function iosSetup() { - const plistPath = path.join('ios', appName, 'Info.plist'); - - const appDelegatePath = path.join('ios', appName, getAppDelegateName()); - - let plistContents = fs.readFileSync(plistPath, "utf8"); - - const dictPlistTag = `\n`; - - const codePushDeploymentKey = iosStagingDeploymentKey || 'deployment-key-here'; - plistContents = plistContents.replace(dictPlistTag, - `\tCodePushDeploymentKey\n\t${codePushDeploymentKey}${dictPlistTag}`); - fs.writeFileSync(plistPath, plistContents); - - let appDelegateContents = fs.readFileSync(appDelegatePath, "utf8"); - const appDelegateHeaderImportStatement = `#import "AppDelegate.h"`; - const codePushHeaderImportStatementFormatted = `\n#import `; - appDelegateContents = appDelegateContents.replace(appDelegateHeaderImportStatement, - `${appDelegateHeaderImportStatement}${codePushHeaderImportStatementFormatted}`); - - - const oldBundleUrl = "[[NSBundle mainBundle] URLForResource:@\"main\" withExtension:@\"jsbundle\"]"; - const codePushBundleUrl = "[CodePush bundleURL]"; - appDelegateContents = appDelegateContents.replace(oldBundleUrl, codePushBundleUrl); - fs.writeFileSync(appDelegatePath, appDelegateContents); - execCommand(`cd ios && pod install && cd ..`); -} - -function execCommand(command) { - console.log(`\n\x1b[2m${command}\x1b[0m\n`); - const result = execSync(command).toString(); - return result; -} diff --git a/Examples/nexpect.js b/Examples/nexpect.js deleted file mode 100644 index 0f37185b4..000000000 --- a/Examples/nexpect.js +++ /dev/null @@ -1,394 +0,0 @@ -/* - * nexpect.js: Top-level include for the `nexpect` module. - * - * (C) 2011, Elijah Insua, Marak Squires, Charlie Robbins. - * - */ - -var spawn = require('child_process').spawn; -var util = require('util'); -var AssertionError = require('assert').AssertionError; - -function chain (context) { - return { - expect: function (expectation) { - var _expect = function _expect (data) { - return testExpectation(data, expectation); - }; - - _expect.shift = true; - _expect.expectation = expectation; - _expect.description = '[expect] ' + expectation; - _expect.requiresInput = true; - context.queue.push(_expect); - - return chain(context); - }, - wait: function (expectation, callback) { - var _wait = function _wait (data) { - var val = testExpectation(data, expectation); - if (val === true && typeof callback === 'function') { - callback(data); - } - return val; - }; - - _wait.shift = false; - _wait.expectation = expectation; - _wait.description = '[wait] ' + expectation; - _wait.requiresInput = true; - context.queue.push(_wait); - return chain(context); - }, - sendline: function (line) { - var _sendline = function _sendline () { - context.process.stdin.write(line + '\n'); - - if (context.verbose) { - process.stdout.write(line + '\n'); - } - }; - - _sendline.shift = true; - _sendline.description = '[sendline] ' + line; - _sendline.requiresInput = false; - context.queue.push(_sendline); - return chain(context); - }, - sendEof: function() { - var _sendEof = function _sendEof () { - context.process.stdin.destroy(); - }; - _sendEof.shift = true; - _sendEof.description = '[sendEof]'; - _sendEof.requiresInput = false; - context.queue.push(_sendEof); - return chain(context); - }, - run: function (callback) { - var errState = null, - responded = false, - stdout = [], - options; - - // - // **onError** - // - // Helper function to respond to the callback with a - // specified error. Kills the child process if necessary. - // - function onError (err, kill) { - if (errState || responded) { - return; - } - - errState = err; - responded = true; - - if (kill) { - try { context.process.kill(); } - catch (ex) { } - } - - callback(err); - } - - // - // **validateFnType** - // - // Helper function to validate the `currentFn` in the - // `context.queue` for the target chain. - // - function validateFnType (currentFn) { - if (typeof currentFn !== 'function') { - // - // If the `currentFn` is not a function, short-circuit with an error. - // - onError(new Error('Cannot process non-function on nexpect stack.'), true); - return false; - } - else if (['_expect', '_sendline', '_wait', '_sendEof'].indexOf(currentFn.name) === -1) { - // - // If the `currentFn` is a function, but not those set by `.sendline()` or - // `.expect()` then short-circuit with an error. - // - onError(new Error('Unexpected context function name: ' + currentFn.name), true); - return false; - } - - return true; - } - - // - // **evalContext** - // - // Core evaluation logic that evaluates the next function in - // `context.queue` against the specified `data` where the last - // function run had `name`. - // - function evalContext (data, name) { - var currentFn = context.queue[0]; - - if (!currentFn || (name === '_expect' && currentFn.name === '_expect')) { - // - // If there is nothing left on the context or we are trying to - // evaluate two consecutive `_expect` functions, return. - // - return; - } - - if (currentFn.shift) { - context.queue.shift(); - } - - if (!validateFnType(currentFn)) { - return; - } - - if (currentFn.name === '_expect') { - // - // If this is an `_expect` function, then evaluate it and attempt - // to evaluate the next function (in case it is a `_sendline` function). - // - return currentFn(data) === true ? - evalContext(data, '_expect') : - onError(createExpectationError(currentFn.expectation, data), true); - } - else if (currentFn.name === '_wait') { - // - // If this is a `_wait` function, then evaluate it and if it returns true, - // then evaluate the function (in case it is a `_sendline` function). - // - if (currentFn(data) === true) { - context.queue.shift(); - evalContext(data, '_expect'); - } - } - else { - // - // If the `currentFn` is any other function then evaluate it - // - currentFn(); - - // Evaluate the next function if it does not need input - var nextFn = context.queue[0]; - if (nextFn && !nextFn.requiresInput) - evalContext(data); - } - } - - // - // **onLine** - // - // Preprocesses the `data` from `context.process` on the - // specified `context.stream` and then evaluates the processed lines: - // - // 1. Stripping ANSI colors (if necessary) - // 2. Removing case sensitivity (if necessary) - // 3. Splitting `data` into multiple lines. - // - function onLine (data) { - data = data.toString(); - - if (context.stripColors) { - data = data.replace(/\u001b\[\d{0,2}m/g, ''); - } - - if (context.ignoreCase) { - data = data.toLowerCase(); - } - - var lines = data.split('\n').filter(function (line) { return line.length > 0; }); - stdout = stdout.concat(lines); - - while (lines.length > 0) { - evalContext(lines.shift(), null); - } - } - - // - // **flushQueue** - // - // Helper function which flushes any remaining functions from - // `context.queue` and responds to the `callback` accordingly. - // - function flushQueue () { - var remainingQueue = context.queue.slice(), - currentFn = context.queue.shift(), - lastLine = stdout[stdout.length - 1]; - - if (!lastLine) { - onError(createUnexpectedEndError( - 'No data from child with non-empty queue.', remainingQueue)); - return false; - } - else if (context.queue.length > 0) { - onError(createUnexpectedEndError( - 'Non-empty queue on spawn exit.', remainingQueue)); - return false; - } - else if (!validateFnType(currentFn)) { - // onError was called - return false; - } - else if (currentFn.name === '_sendline') { - onError(new Error('Cannot call sendline after the process has exited')); - return false; - } - else if (currentFn.name === '_wait' || currentFn.name === '_expect') { - if (currentFn(lastLine) !== true) { - onError(createExpectationError(currentFn.expectation, lastLine)); - return false; - } - } - - return true; - } - - // - // **onData** - // - // Helper function for writing any data from a stream - // to `process.stdout`. - // - function onData (data) { - process.stdout.write(data); - } - - options = { - cwd: context.cwd, - env: context.env - }; - - // - // Spawn the child process and begin processing the target - // stream for this chain. - // - if (!/^win/.test(process.platform)) { - context.process = spawn(context.command, context.params, options); - } else { - context.process = spawn('cmd', ['/c', `${context.command}`].concat(context.params), options); - } - - if (context.verbose) { - context.process.stdout.on('data', onData); - context.process.stderr.on('data', onData); - } - - if (context.stream === 'all') { - context.process.stdout.on('data', onLine); - context.process.stderr.on('data', onLine); - } else { - context.process[context.stream].on('data', onLine); - } - - context.process.on('error', onError); - - // - // When the process exits, check the output `code` and `signal`, - // flush `context.queue` (if necessary) and respond to the callback - // appropriately. - // - context.process.on('close', function (code, signal) { - if (code === 127) { - // XXX(sam) Not how node works (anymore?), 127 is what /bin/sh returns, - // but it appears node does not, or not in all conditions, blithely - // return 127 to user, it emits an 'error' from the child_process. - - // - // If the response code is `127` then `context.command` was not found. - // - return onError(new Error('Command not found: ' + context.command)); - } - else if (context.queue.length && !flushQueue()) { - // if flushQueue returned false, onError was called - return; - } - - callback(null, stdout, signal || code); - }); - - return context.process; - } - }; -} - -function testExpectation(data, expectation) { - if (util.isRegExp(expectation)) { - return expectation.test(data); - } else { - return data.indexOf(expectation) > -1; - } -} - -function createUnexpectedEndError(message, remainingQueue) { - var desc = remainingQueue.map(function(it) { return it.description; }); - var msg = message + '\n' + desc.join('\n'); - return new AssertionError({ - message: msg, - expected: [], - actual: desc - }); -} - -function createExpectationError(expected, actual) { - var expectation; - if (util.isRegExp(expected)) - expectation = 'to match ' + expected; - else - expectation = 'to contain ' + JSON.stringify(expected); - - var err = new AssertionError({ - message: util.format('expected %j %s', actual, expectation), - actual: actual, - expected: expected - }); - return err; -} - -function nspawn (command, params, options) { - if (arguments.length === 2) { - if (Array.isArray(arguments[1])) { - options = {}; - } - else { - options = arguments[1]; - params = null; - } - } - - if (Array.isArray(command)) { - params = command; - command = params.shift(); - } - else if (typeof command === 'string') { - command = command.split(' '); - params = params || command.slice(1); - command = command[0]; - } - - options = options || {}; - // eslint-disable-next-line no-global-assign - context = { - command: command, - cwd: options.cwd || undefined, - env: options.env || undefined, - ignoreCase: options.ignoreCase, - params: params, - queue: [], - stream: options.stream || 'stdout', - stripColors: options.stripColors, - verbose: options.verbose - }; - - return chain(context); -} - -// -// Export the core `nspawn` function as well as `nexpect.nspawn` for -// backwards compatibility. -// -module.exports.spawn = nspawn; -module.exports.nspawn = { - spawn: nspawn -}; diff --git a/code-push-plugin-testing-framework/package.json b/code-push-plugin-testing-framework/package.json deleted file mode 100644 index eafc11471..000000000 --- a/code-push-plugin-testing-framework/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "code-push-plugin-testing-framework", - "version": "0.0.1", - "description": "Plugin Testing Framework for CodePush Plugins", - "main": "script/index.js", - "scripts": { - "test": "gulp" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/microsoft/code-push.git" - }, - "author": { - "name": "Microsoft Corporation" - }, - "license": "MIT", - "homepage": "https://microsoft.github.io/code-push", - "dependencies": { - "@types/uuid": "^8.3.1", - "base-64": "^1.0.0", - "mocha": "latest", - "mocha-junit-reporter": "latest", - "q": "^1.5.1", - "replace": "latest", - "superagent": "^6.1.0", - "superagent-proxy": "^3.0.0", - "uuid": "^8.3.2" - }, - "bugs": { - "url": "https://github.com/microsoft/code-push/issues" - }, - "readme": "ERROR: No README data found!", - "_id": "code-push-plugin-testing-framework@0.0.1", - "_shasum": "6ea33a661710628af266d714949fe95f88d71f0d", - "_from": "../code-push/plugin-testing-framework/bin", - "_resolved": "file:../code-push/plugin-testing-framework/bin" -} diff --git a/code-push-plugin-testing-framework/script/index.js b/code-push-plugin-testing-framework/script/index.js deleted file mode 100644 index 0dc995d00..000000000 --- a/code-push-plugin-testing-framework/script/index.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -var Platform = require("./platform"); -exports.Platform = Platform; -var PluginTestingFramework = require("./test"); -exports.PluginTestingFramework = PluginTestingFramework; -var projectManager_1 = require("./projectManager"); -exports.ProjectManager = projectManager_1.ProjectManager; -exports.setupTestRunScenario = projectManager_1.setupTestRunScenario; -exports.setupUpdateScenario = projectManager_1.setupUpdateScenario; -var ServerUtil = require("./serverUtil"); -exports.ServerUtil = ServerUtil; -var testBuilder_1 = require("./testBuilder"); -exports.TestBuilder = testBuilder_1.TestBuilder; -var TestConfig = require("./testConfig"); -exports.TestConfig = TestConfig; -var testUtil_1 = require("./testUtil"); -exports.TestUtil = testUtil_1.TestUtil; diff --git a/code-push-plugin-testing-framework/script/platform.js b/code-push-plugin-testing-framework/script/platform.js deleted file mode 100644 index 351cbceb3..000000000 --- a/code-push-plugin-testing-framework/script/platform.js +++ /dev/null @@ -1,413 +0,0 @@ -"use strict"; -var Q = require("q"); -var testUtil_1 = require("./testUtil"); -////////////////////////////////////////////////////////////////////////////////////////// -// PLATFORMS -/** - * Android implementations of IPlatform. - */ -var Android = (function () { - function Android(emulatorManager) { - this.emulatorManager = emulatorManager; - } - /** - * Gets the platform name. (e.g. "android" for the Android platform). - */ - Android.prototype.getName = function () { - return "android"; - }; - /** - * The command line flag used to determine whether or not this platform should run. - * Runs when the flag is present, doesn't run otherwise. - */ - Android.prototype.getCommandLineFlagName = function () { - return "--android"; - }; - /** - * Gets the server url used for testing. - */ - Android.prototype.getServerUrl = function () { - if (!this.serverUrl) - this.serverUrl = process.env.ANDROID_SERVER ? process.env.ANDROID_SERVER : Android.DEFAULT_ANDROID_SERVER_URL; - return this.serverUrl; - }; - /** - * Gets an IEmulatorManager that is used to control the emulator during the tests. - */ - Android.prototype.getEmulatorManager = function () { - return this.emulatorManager; - }; - /** - * Gets the default deployment key. - */ - Android.prototype.getDefaultDeploymentKey = function () { - return "mock-android-deployment-key"; - }; - Android.DEFAULT_ANDROID_SERVER_URL = "http://10.0.2.2:3001"; - return Android; -}()); -exports.Android = Android; -/** - * IOS implementation of IPlatform. - */ -var IOS = (function () { - function IOS(emulatorManager) { - this.emulatorManager = emulatorManager; - } - /** - * Gets the platform name. (e.g. "android" for the Android platform). - */ - IOS.prototype.getName = function () { - return "ios"; - }; - /** - * The command line flag used to determine whether or not this platform should run. - * Runs when the flag is present, doesn't run otherwise. - */ - IOS.prototype.getCommandLineFlagName = function () { - return "--ios"; - }; - /** - * Gets the server url used for testing. - */ - IOS.prototype.getServerUrl = function () { - if (!this.serverUrl) - this.serverUrl = process.env.IOS_SERVER ? process.env.IOS_SERVER : IOS.DEFAULT_IOS_SERVER_URL; - - return this.serverUrl; - }; - /** - * Gets an IEmulatorManager that is used to control the emulator during the tests. - */ - IOS.prototype.getEmulatorManager = function () { - return this.emulatorManager; - }; - /** - * Gets the default deployment key. - */ - IOS.prototype.getDefaultDeploymentKey = function () { - return "mock-ios-deployment-key"; - }; - IOS.DEFAULT_IOS_SERVER_URL = "http://127.0.0.1:3000"; - return IOS; -}()); -exports.IOS = IOS; -////////////////////////////////////////////////////////////////////////////////////////// -// EMULATOR MANAGERS -// bootEmulatorInternal constants -var emulatorMaxReadyAttempts = 50; -var emulatorReadyCheckDelayMs = 5 * 1000; -/** - * Helper function for EmulatorManager implementations to use to boot an emulator with a given platformName and check, start, and kill methods. - */ -function bootEmulatorInternal(platformName, restartEmulators, targetEmulator, checkEmulator, startEmulator, killEmulator) { - var deferred = Q.defer(); - console.log("Setting up " + platformName + " emulator."); - function onEmulatorReady() { - console.log(platformName + " emulator is ready!"); - deferred.resolve(undefined); - return deferred.promise; - } - // Called to check if the emulator for the platform is initialized. - function checkEmulatorReady() { - var checkDeferred = Q.defer(); - console.log("Checking if " + platformName + " emulator is ready yet..."); - // Dummy command that succeeds if emulator is ready and fails otherwise. - checkEmulator(targetEmulator) - .then(function () { - checkDeferred.resolve(undefined); - }, function (error) { - console.info(error); - console.log(platformName + " emulator is not ready yet!"); - checkDeferred.reject(error); - }); - return checkDeferred.promise; - } - var emulatorReadyAttempts = 0; - // Loops checks to see if the emulator is ready and eventually fails after surpassing emulatorMaxReadyAttempts. - function checkEmulatorReadyLooper() { - var looperDeferred = Q.defer(); - emulatorReadyAttempts++; - if (emulatorReadyAttempts > emulatorMaxReadyAttempts) { - console.log(platformName + " emulator is not ready after " + emulatorMaxReadyAttempts + " attempts, abort."); - deferred.reject(platformName + " emulator failed to boot."); - looperDeferred.resolve(undefined); - } - setTimeout(function () { - checkEmulatorReady() - .then(function () { - looperDeferred.resolve(undefined); - onEmulatorReady(); - }, function () { - return checkEmulatorReadyLooper().then(function () { looperDeferred.resolve(undefined); }, function () { looperDeferred.reject(undefined); }); - }); - }, emulatorReadyCheckDelayMs); - return looperDeferred.promise; - } - // Starts and loops the emulator. - function startEmulatorAndLoop() { - console.log("Booting " + platformName + " emulator named " + targetEmulator + "."); - startEmulator(targetEmulator).catch(function (error) { console.log(error); deferred.reject(error); }); - return checkEmulatorReadyLooper(); - } - var promise; - if (restartEmulators) { - console.log("Killing " + platformName + " emulator."); - promise = killEmulator().catch(function () { return null; }).then(startEmulatorAndLoop); - } - else { - promise = checkEmulatorReady().then(onEmulatorReady, startEmulatorAndLoop); - } - return deferred.promise; -} -var AndroidEmulatorManager = (function () { - function AndroidEmulatorManager() { - } - /** - * Returns the target emulator, which is specified through the command line. - */ - AndroidEmulatorManager.prototype.getTargetEmulator = function () { - let _this = this; - if (this.targetEmulator) - return Q(this.targetEmulator); - else { - const deferred = Q.defer(); - const targetAndroidEmulator = process.env.ANDROID_EMU; - if (!targetAndroidEmulator) { - // If no Android simulator is specified, get the most recent Android simulator to run tests on. - testUtil_1.TestUtil.getProcessOutput("emulator -list-avds", { noLogCommand: true, noLogStdOut: true, noLogStdErr: true }) - .then((Devices) => { - const listOfDevices = Devices.trim().split("\n"); - deferred.resolve(listOfDevices[listOfDevices.length - 1]); - }, (error) => { - deferred.reject(error); - }); - } - else { - // Use the simulator specified on the command line. - deferred.resolve(targetAndroidEmulator); - } - return deferred.promise - .then((targetEmulator) => { - _this.targetEmulator = targetEmulator; - console.log("Using Android simulator named " + _this.targetEmulator); - return _this.targetEmulator; - }); - } - }; - /** - * Boots the target emulator. - */ - AndroidEmulatorManager.prototype.bootEmulator = function (restartEmulators) { - function checkAndroidEmulator(androidEmulatorName) { - // A command that does nothing but only succeeds if the emulator is running. - // List all of the packages on the device. - return testUtil_1.TestUtil.getProcessOutput("adb shell pm list packages", { noLogCommand: true, noLogStdOut: true, noLogStdErr: true }).then(function () { return null; }); - } - function startAndroidEmulator(androidEmulatorName) { - const androidEmulatorCommand = `emulator @${androidEmulatorName}`; - let osSpecificCommand = ""; - if (process.platform === "darwin") { - osSpecificCommand = `${androidEmulatorCommand} &`; - } else { - osSpecificCommand = `START /B ${androidEmulatorCommand}`; - } - return testUtil_1.TestUtil.getProcessOutput(osSpecificCommand, { noLogStdErr: true, timeout: 5000 }); - } - function killAndroidEmulator() { - return testUtil_1.TestUtil.getProcessOutput("adb emu kill").then(function () { return null; }); - } - return this.getTargetEmulator() - .then(function (targetEmulator) { - return bootEmulatorInternal("Android", restartEmulators, targetEmulator, checkAndroidEmulator, startAndroidEmulator, killAndroidEmulator); - }); - }; - /** - * Launches an already installed application by app id. - */ - AndroidEmulatorManager.prototype.launchInstalledApplication = function (appId) { - return testUtil_1.TestUtil.getProcessOutput("adb shell monkey -p " + appId + " -c android.intent.category.LAUNCHER 1").then(function () { return null; }); - }; - /** - * Ends a running application given its app id. - */ - AndroidEmulatorManager.prototype.endRunningApplication = function (appId) { - return testUtil_1.TestUtil.getProcessOutput("adb shell am force-stop " + appId).then(function () { return Q.delay(10000); }); - }; - /** - * Restarts an already installed application by app id. - */ - AndroidEmulatorManager.prototype.restartApplication = function (appId) { - var _this = this; - return this.endRunningApplication(appId) - .then(function () { - // Wait for a 1 second before restarting. - return Q.delay(1000); - }) - .then(function () { - return _this.launchInstalledApplication(appId); - }); - }; - /** - * Navigates away from the current app, waits for a delay (defaults to 1 second), then navigates to the specified app. - */ - AndroidEmulatorManager.prototype.resumeApplication = function (appId, delayBeforeResumingMs) { - var _this = this; - if (delayBeforeResumingMs === void 0) { delayBeforeResumingMs = 1000; } - // Open a default Android app (for example, settings). - return this.launchInstalledApplication("com.android.settings") - .then(function () { - console.log("Waiting for " + delayBeforeResumingMs + "ms before resuming the test application."); - return Q.delay(delayBeforeResumingMs); - }) - .then(function () { - // Reopen the app. - return _this.launchInstalledApplication(appId); - }); - }; - /** - * Prepares the emulator for a test. - */ - AndroidEmulatorManager.prototype.prepareEmulatorForTest = function (appId) { - return this.endRunningApplication(appId) - .then(function () { - return commandWithCheckAppExistence("adb shell pm clear", appId); - }); - }; - /** - * Uninstalls the app from the emulator. - */ - AndroidEmulatorManager.prototype.uninstallApplication = function (appId) { - return commandWithCheckAppExistence("adb uninstall", appId); - }; - return AndroidEmulatorManager; -}()); -exports.AndroidEmulatorManager = AndroidEmulatorManager; -var IOSEmulatorManager = (function () { - function IOSEmulatorManager() { - } - /** - * Returns the target emulator, which is specified through the command line. - */ - IOSEmulatorManager.prototype.getTargetEmulator = function () { - let _this = this; - if (this.targetEmulator) - return Q(this.targetEmulator); - else { - let deferred = Q.defer(); - let targetIOSEmulator = process.env.IOS_EMU; - if (!targetIOSEmulator) { - // If no iOS simulator is specified, get the most recent iOS simulator to run tests on. - testUtil_1.TestUtil.getProcessOutput("xcrun simctl list", { noLogCommand: true, noLogStdOut: true, noLogStdErr: true }) - .then((listOfDevicesWithDevicePairs) => { - let listOfDevices = listOfDevicesWithDevicePairs.slice(listOfDevicesWithDevicePairs.indexOf("-- iOS"), listOfDevicesWithDevicePairs.indexOf("-- tvOS")); - let phoneDevice = /iPhone\ \S*\ ?.*?\(([0-9A-Z-]*)\)/g; - let match = phoneDevice.exec(listOfDevices); - deferred.resolve(match[1]); - }, (error) => { - deferred.reject(error); - }); - } - else { - // Use the simulator specified on the command line. - deferred.resolve(targetIOSEmulator); - } - return deferred.promise - .then((targetEmulator) => { - _this.targetEmulator = targetEmulator; - console.log("Using iOS simulator named " + _this.targetEmulator); - return _this.targetEmulator; - }); - } - }; - /** - * Boots the target emulator. - */ - IOSEmulatorManager.prototype.bootEmulator = function (restartEmulators) { - function checkIOSEmulator(iOSEmulatorId) { - // A command that does nothing but only succeeds if the emulator is running. - return testUtil_1.TestUtil.getProcessOutput("xcrun simctl getenv booted SIMULATOR_UDID", { noLogCommand: true, noLogStdOut: true, noLogStdErr: true }).then(function (simUdid) { - return simUdid.trim() == iOSEmulatorId.trim() ? true : Promise.reject(new Error('Waiting for device to boot')); - }); - } - function startIOSEmulator(iOSEmulatorId) { - return testUtil_1.TestUtil.getProcessOutput("xcrun simctl boot " + iOSEmulatorId, { noLogStdErr: true }) - .catch(function (error) { return undefined; /* Always fails because we do not specify a template, which is not necessary to just start the emulator */ }).then(function () { return null; }); - } - function killIOSEmulator() { - return testUtil_1.TestUtil.getProcessOutput("xcrun simctl shutdown all").then(function () { return null; }); - } - return this.getTargetEmulator() - .then(function (targetEmulator) { - return bootEmulatorInternal("iOS", restartEmulators, targetEmulator, checkIOSEmulator, startIOSEmulator, killIOSEmulator); - }); - }; - /** - * Launches an already installed application by app id. - */ - IOSEmulatorManager.prototype.launchInstalledApplication = function (appId) { - return testUtil_1.TestUtil.getProcessOutput("xcrun simctl launch booted " + appId, undefined).then(function () { return null; }); - }; - /** - * Ends a running application given its app id. - */ - IOSEmulatorManager.prototype.endRunningApplication = function (appId) { - return testUtil_1.TestUtil.getProcessOutput("xcrun simctl terminate booted " + appId, undefined).then(function () { return null; }) - }; - /** - * Restarts an already installed application by app id. - */ - IOSEmulatorManager.prototype.restartApplication = function (appId) { - var _this = this; - return this.endRunningApplication(appId) - .then(function () { - // Wait for a second before restarting. - return Q.delay(1000); - }) - .then(function () { return _this.launchInstalledApplication(appId); }); - }; - /** - * Navigates away from the current app, waits for a delay (defaults to 1 second), then navigates to the specified app. - */ - IOSEmulatorManager.prototype.resumeApplication = function (appId, delayBeforeResumingMs) { - var _this = this; - if (delayBeforeResumingMs === void 0) { delayBeforeResumingMs = 1000; } - // Open a default iOS app (for example, settings). - return this.launchInstalledApplication("com.apple.Preferences") - .then(function () { - console.log("Waiting for " + delayBeforeResumingMs + "ms before resuming the test application."); - return Q.delay(delayBeforeResumingMs); - }) - .then(function () { - // Reopen the app. - return _this.launchInstalledApplication(appId); - }); - }; - /** - * Prepares the emulator for a test. - */ - IOSEmulatorManager.prototype.prepareEmulatorForTest = function (appId) { - return this.endRunningApplication(appId); - }; - /** - * Uninstalls the app from the emulator. - */ - IOSEmulatorManager.prototype.uninstallApplication = function (appId) { - return testUtil_1.TestUtil.getProcessOutput("xcrun simctl uninstall booted " + appId).then(function () { return null; }); - }; - return IOSEmulatorManager; -}()); -exports.IOSEmulatorManager = IOSEmulatorManager; - -function commandWithCheckAppExistence(command, appId) { - return testUtil_1.TestUtil.getProcessOutput("adb shell pm list packages", { noLogCommand: true, noLogStdOut: true, noLogStdErr: true }) - .then((output) => { - return output.includes(appId); - }).then((isAppExist) => { - if (isAppExist) { - return testUtil_1.TestUtil.getProcessOutput(`${command} ${appId}`).then(function () { return null; }); - } - console.log(`Command "${command}" is skipped because the application has not yet been installed`) - return null; - }); -} diff --git a/code-push-plugin-testing-framework/script/projectManager.js b/code-push-plugin-testing-framework/script/projectManager.js deleted file mode 100644 index 3c4a5b96f..000000000 --- a/code-push-plugin-testing-framework/script/projectManager.js +++ /dev/null @@ -1,81 +0,0 @@ -"use strict"; -var TestConfig = require("./testConfig"); -/** - * In charge of project related operations. - */ -var ProjectManager = (function () { - function ProjectManager() { - } - //// ABSTRACT METHODS - // (not actually abstract because there are some issues with our dts generator that causes it to incorrectly generate abstract classes) - /** - * Returns the name of the plugin being tested, for example Cordova or React-Native. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.getPluginName = function () { throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; }; - /** - * Creates a new test application at the specified path, and configures it - * with the given server URL, android and ios deployment keys. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.setupProject = function (projectDirectory, templatePath, appName, appNamespace, version) { - if (version === void 0) { version = ProjectManager.DEFAULT_APP_VERSION; } - throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; - }; - /** - * Sets up the scenario for a test in an already existing project. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.setupScenario = function (projectDirectory, appId, templatePath, jsPath, targetPlatform, version) { - if (version === void 0) { version = ProjectManager.DEFAULT_APP_VERSION; } - throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; - }; - /** - * Creates a CodePush update package zip for a project. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.createUpdateArchive = function (projectDirectory, targetPlatform, isDiff) { throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; }; - /** - * Prepares a specific platform for tests. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.preparePlatform = function (projectDirectory, targetPlatform) { throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; }; - /** - * Cleans up a specific platform after tests. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.cleanupAfterPlatform = function (projectDirectory, targetPlatform) { throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; }; - /** - * Runs the test app on the given target / platform. - * - * Overwrite this in your implementation! - */ - ProjectManager.prototype.runApplication = function (projectDirectory, targetPlatform) { throw ProjectManager.NOT_IMPLEMENTED_ERROR_MSG; }; - ProjectManager.DEFAULT_APP_VERSION = "Store version"; - ProjectManager.NOT_IMPLEMENTED_ERROR_MSG = "This method is unimplemented! Please extend ProjectManager and overwrite it!"; - return ProjectManager; -}()); -exports.ProjectManager = ProjectManager; -////////////////////////////////////////////////////////////////////////////////////////// -// Wrapper functions for simpler code in test cases. -/** - * Wrapper for ProjectManager.setupScenario in the TestRun directory. - */ -function setupTestRunScenario(projectManager, targetPlatform, scenarioJsPath, version) { - return projectManager.setupScenario(TestConfig.testRunDirectory, TestConfig.TestNamespace, TestConfig.templatePath, scenarioJsPath, targetPlatform, version); -} -exports.setupTestRunScenario = setupTestRunScenario; -/** - * Creates an update and zip for the test app using the specified scenario and version. - */ -function setupUpdateScenario(projectManager, targetPlatform, scenarioJsPath, version) { - return projectManager.setupScenario(TestConfig.updatesDirectory, TestConfig.TestNamespace, TestConfig.templatePath, scenarioJsPath, targetPlatform, version) - .then(projectManager.createUpdateArchive.bind(projectManager, TestConfig.updatesDirectory, targetPlatform)); -} -exports.setupUpdateScenario = setupUpdateScenario; diff --git a/code-push-plugin-testing-framework/script/serverUtil.js b/code-push-plugin-testing-framework/script/serverUtil.js deleted file mode 100644 index 80503628d..000000000 --- a/code-push-plugin-testing-framework/script/serverUtil.js +++ /dev/null @@ -1,248 +0,0 @@ -"use strict"; -// IMPORTS -var assert = require("assert"); -var bodyParser = require("body-parser"); -var express = require("express"); -var Q = require("q"); -////////////////////////////////////////////////////////////////////////////////////////// -// Use these functions to set up and shut down the server. -/** - * Sets up the server that the test app uses to send test messages and check for and download updates. - */ -function setupServer(targetPlatform) { - console.log("Setting up server at " + targetPlatform.getServerUrl()); - var app = express(); - app.use(bodyParser.json()); - app.use(bodyParser.urlencoded({ extended: true })); - app.use(function (req, res, next) { - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Access-Control-Allow-Methods", "*"); - res.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept, X-CodePush-SDK-Version"); - next(); - }); - app.get("/v0.1/public/codepush/update_check", function (req, res) { - exports.updateCheckCallback && exports.updateCheckCallback(req); - res.send(exports.updateResponse); - console.log("Update check called from the app."); - console.log("Request: " + JSON.stringify(req.query)); - console.log("Response: " + JSON.stringify(exports.updateResponse)); - }); - app.get("/v0.1/public/codepush/report_status/download", function (req, res) { - console.log("Application downloading the package."); - res.download(exports.updatePackagePath); - }); - app.post("/reportTestMessage", function (req, res) { - console.log("Application reported a test message."); - console.log("Body: " + JSON.stringify(req.body)); - if (!exports.testMessageResponse) { - console.log("Sending OK"); - res.sendStatus(200); - } - else { - console.log("Sending body: " + exports.testMessageResponse); - res.status(200).send(exports.testMessageResponse); - } - exports.testMessageCallback && exports.testMessageCallback(req.body); - }); - var serverPortRegEx = /:([0-9]+)/; - exports.server = app.listen(+targetPlatform.getServerUrl().match(serverPortRegEx)[1]); -} -exports.setupServer = setupServer; -/** - * Closes the server. - */ -function cleanupServer() { - if (exports.server) { - exports.server.close(); - exports.server = undefined; - } -} -exports.cleanupServer = cleanupServer; -////////////////////////////////////////////////////////////////////////////////////////// -// Classes and methods used for sending mock responses to the app. -/** - * Class used to mock the codePush.checkForUpdate() response from the server. - */ -var CheckForUpdateResponseMock = (function () { - function CheckForUpdateResponseMock() { - } - return CheckForUpdateResponseMock; -}()); -exports.CheckForUpdateResponseMock = CheckForUpdateResponseMock; -/** - * The model class of the codePush.checkForUpdate() request to the server. - */ -var UpdateCheckRequestMock = (function () { - function UpdateCheckRequestMock() { - } - return UpdateCheckRequestMock; -}()); -exports.UpdateCheckRequestMock = UpdateCheckRequestMock; -/** - * Returns a default empty response to give to the app in a checkForUpdate request - */ -function createDefaultResponse() { - var defaultResponse = new CheckForUpdateResponseMock(); - defaultResponse.download_url = ""; - defaultResponse.is_disabled = false; - defaultResponse.description = ""; - defaultResponse.is_available = false; - defaultResponse.is_mandatory = false; - defaultResponse.target_binary_range = ""; - defaultResponse.package_hash = ""; - defaultResponse.label = ""; - defaultResponse.package_size = 0; - defaultResponse.should_run_binary_version = false; - defaultResponse.update_app_version = false; - return defaultResponse; -} -exports.createDefaultResponse = createDefaultResponse; -/** - * Returns a default update response to give to the app in a checkForUpdate request - */ -function createUpdateResponse(mandatory, targetPlatform, randomHash) { - if (mandatory === void 0) { mandatory = false; } - if (randomHash === void 0) { randomHash = true; } - var updateResponse = new CheckForUpdateResponseMock(); - updateResponse.is_available = true; - updateResponse.is_disabled = false; - updateResponse.target_binary_range = "1.0.0"; - updateResponse.download_url = "mock.url/v0.1/public/codepush/report_status/download"; - updateResponse.is_mandatory = mandatory; - updateResponse.label = "mock-update"; - updateResponse.package_hash = "12345-67890"; - updateResponse.package_size = 12345; - updateResponse.should_run_binary_version = false; - updateResponse.update_app_version = false; - if (!!targetPlatform) - updateResponse.download_url = targetPlatform.getServerUrl() + "/v0.1/public/codepush/report_status/download"; - // We need unique hashes to avoid conflicts. - if (randomHash) { - updateResponse.package_hash = "randomHash-" + Math.floor(Math.random() * 10000); - } - return updateResponse; -} -exports.createUpdateResponse = createUpdateResponse; -/** - * Returns a promise that waits for the next set of test messages sent by the app and resolves if that they are equal to the expected messages or rejects if they are not. - */ -function expectTestMessages(expectedMessages) { - var deferred = Q.defer(); - var messageIndex = 0; - var lastRequestBody = null; - exports.testMessageCallback = function (requestBody) { - try { - console.log("Message index: " + messageIndex); - // We should ignore duplicated requests. It is only CI issue. - if (lastRequestBody === null || !areEqual(requestBody, lastRequestBody)) { - if (typeof expectedMessages[messageIndex] === "string") { - assert.equal(requestBody.message, expectedMessages[messageIndex]); - } - else { - assert(areEqual(requestBody, expectedMessages[messageIndex])); - } - - lastRequestBody = requestBody; - - /* end of message array */ - if (++messageIndex === expectedMessages.length) { - deferred.resolve(undefined); - } - } - } - catch (e) { - deferred.reject(e); - } - }; - return deferred.promise; -} -exports.expectTestMessages = expectTestMessages; -; -////////////////////////////////////////////////////////////////////////////////////////// -// Test messages used by the test app to send state information to the server. -/** - * Contains all the messages sent from the application to the mock server during tests. - */ -var TestMessage = (function () { - function TestMessage() { - } - TestMessage.CHECK_UP_TO_DATE = "CHECK_UP_TO_DATE"; - TestMessage.CHECK_UPDATE_AVAILABLE = "CHECK_UPDATE_AVAILABLE"; - TestMessage.CHECK_ERROR = "CHECK_ERROR"; - TestMessage.DOWNLOAD_SUCCEEDED = "DOWNLOAD_SUCCEEDED"; - TestMessage.DOWNLOAD_ERROR = "DOWNLOAD_ERROR"; - TestMessage.UPDATE_INSTALLED = "UPDATE_INSTALLED"; - TestMessage.INSTALL_ERROR = "INSTALL_ERROR"; - TestMessage.DEVICE_READY_AFTER_UPDATE = "DEVICE_READY_AFTER_UPDATE"; - TestMessage.UPDATE_FAILED_PREVIOUSLY = "UPDATE_FAILED_PREVIOUSLY"; - TestMessage.NOTIFY_APP_READY_SUCCESS = "NOTIFY_APP_READY_SUCCESS"; - TestMessage.NOTIFY_APP_READY_FAILURE = "NOTIFY_APP_READY_FAILURE"; - TestMessage.SKIPPED_NOTIFY_APPLICATION_READY = "SKIPPED_NOTIFY_APPLICATION_READY"; - TestMessage.SYNC_STATUS = "SYNC_STATUS"; - TestMessage.RESTART_SUCCEEDED = "RESTART_SUCCEEDED"; - TestMessage.RESTART_FAILED = "RESTART_FAILED"; - TestMessage.PENDING_PACKAGE = "PENDING_PACKAGE"; - TestMessage.CURRENT_PACKAGE = "CURRENT_PACKAGE"; - TestMessage.SYNC_UP_TO_DATE = 0; - TestMessage.SYNC_UPDATE_INSTALLED = 1; - TestMessage.SYNC_UPDATE_IGNORED = 2; - TestMessage.SYNC_ERROR = 3; - TestMessage.SYNC_IN_PROGRESS = 4; - TestMessage.SYNC_CHECKING_FOR_UPDATE = 5; - TestMessage.SYNC_AWAITING_USER_ACTION = 6; - TestMessage.SYNC_DOWNLOADING_PACKAGE = 7; - TestMessage.SYNC_INSTALLING_UPDATE = 8; - return TestMessage; -}()); -exports.TestMessage = TestMessage; -/** - * Contains all the messages sent from the mock server back to the application during tests. - */ -var TestMessageResponse = (function () { - function TestMessageResponse() { - } - TestMessageResponse.SKIP_NOTIFY_APPLICATION_READY = "SKIP_NOTIFY_APPLICATION_READY"; - return TestMessageResponse; -}()); -exports.TestMessageResponse = TestMessageResponse; -/** - * Defines the messages sent from the application to the mock server during tests. - */ -var AppMessage = (function () { - function AppMessage(message, args) { - this.message = message; - this.args = args; - } - AppMessage.fromString = function (message) { - return new AppMessage(message, undefined); - }; - return AppMessage; -}()); -exports.AppMessage = AppMessage; -/** - * Checks if two messages are equal. - */ -function areEqual(m1, m2) { - /* compare objects */ - if (m1 === m2) { - return true; - } - /* compare messages */ - if (!m1 || !m2 || m1.message !== m2.message) { - return false; - } - /* compare arguments */ - if (m1.args === m2.args) { - return true; - } - if (!m1.args || !m2.args || m1.args.length !== m2.args.length) { - return false; - } - for (var i = 0; i < m1.args.length; i++) { - if (m1.args[i] !== m2.args[i]) { - return false; - } - } - return true; -} -exports.areEqual = areEqual; diff --git a/code-push-plugin-testing-framework/script/test.js b/code-push-plugin-testing-framework/script/test.js deleted file mode 100644 index 2a209edc9..000000000 --- a/code-push-plugin-testing-framework/script/test.js +++ /dev/null @@ -1,104 +0,0 @@ -"use strict"; -var Q = require("q"); -var ServerUtil = require("./serverUtil"); -var testBuilder_1 = require("./testBuilder"); -var TestConfig = require("./testConfig"); -var testUtil_1 = require("./testUtil"); -////////////////////////////////////////////////////////////////////////////////////////// -/** - * Call this function to initialize the automated tests. - */ -function initializeTests(projectManager, supportedTargetPlatforms, describeTests) { - // DETERMINE PLATFORMS TO TEST // - /** The platforms to test on. */ - var targetPlatforms = []; - supportedTargetPlatforms.forEach(function (supportedPlatform) { - if (testUtil_1.TestUtil.readMochaCommandLineFlag(supportedPlatform.getCommandLineFlagName())) - targetPlatforms.push(supportedPlatform); - }); - // Log current configuration - console.log("Initializing tests for " + testUtil_1.TestUtil.getPluginName()); - console.log(TestConfig.TestAppName + "\n" + TestConfig.TestNamespace); - console.log("Testing " + TestConfig.thisPluginPath + "."); - targetPlatforms.forEach(function (platform) { - console.log("On " + platform.getName()); - }); - console.log("test run directory = " + TestConfig.testRunDirectory); - console.log("updates directory = " + TestConfig.updatesDirectory); - if (TestConfig.onlyRunCoreTests) - console.log("--only running core tests--"); - if (TestConfig.shouldSetup) - console.log("--setting up--"); - if (TestConfig.restartEmulators) - console.log("--restarting emulators--"); - // FUNCTIONS // - function cleanupTest() { - console.log("Cleaning up!"); - ServerUtil.updateResponse = undefined; - ServerUtil.testMessageCallback = undefined; - ServerUtil.updateCheckCallback = undefined; - ServerUtil.testMessageResponse = undefined; - } - /** - * Sets up tests for each platform. - * Creates the test project directory and the test update directory. - * Starts required emulators. - */ - function setupTests() { - it("sets up tests correctly", function (done) { - var promises = []; - targetPlatforms.forEach(function (platform) { - promises.push(platform.getEmulatorManager().bootEmulator(TestConfig.restartEmulators)); - }); - console.log("Building test project."); - // create the test project - promises.push(createTestProject(TestConfig.testRunDirectory) - .then(function () { - console.log("Building update project."); - // create the update project - return createTestProject(TestConfig.updatesDirectory); - }).then(function () { return null; })); - Q.all(promises).then(function () { done(); }, function (error) { done(error); }); - }); - } - /** - * Creates a test project directory at the given path. - */ - function createTestProject(directory) { - return projectManager.setupProject(directory, TestConfig.templatePath, TestConfig.TestAppName, TestConfig.TestNamespace); - } - /** - * Creates and runs the tests from the projectManager and TestBuilderDescribe objects passed to initializeTests. - */ - function createAndRunTests(targetPlatform) { - describe("CodePush", function () { - before(function () { - ServerUtil.setupServer(targetPlatform); - return targetPlatform.getEmulatorManager().uninstallApplication(TestConfig.TestNamespace) - .then(projectManager.preparePlatform.bind(projectManager, TestConfig.testRunDirectory, targetPlatform)) - .then(projectManager.preparePlatform.bind(projectManager, TestConfig.updatesDirectory, targetPlatform)); - }); - after(function () { - ServerUtil.cleanupServer(); - return projectManager.cleanupAfterPlatform(TestConfig.testRunDirectory, targetPlatform).then(projectManager.cleanupAfterPlatform.bind(projectManager, TestConfig.updatesDirectory, targetPlatform)); - }); - testBuilder_1.TestContext.projectManager = projectManager; - testBuilder_1.TestContext.targetPlatform = targetPlatform; - // Build the tests. - describeTests(projectManager, targetPlatform); - }); - } - // BEGIN TESTING // - describe("CodePush " + projectManager.getPluginName() + " Plugin", function () { - this.timeout(100 * 60 * 1000); - if (TestConfig.shouldSetup) - describe("Setting Up For Tests", function () { return setupTests(); }); - else { - targetPlatforms.forEach(function (platform) { - var prefix = (TestConfig.onlyRunCoreTests ? "Core Tests " : "Tests ") + TestConfig.thisPluginPath + " on "; - describe(prefix + platform.getName(), function () { return createAndRunTests(platform); }); - }); - } - }); -} -exports.initializeTests = initializeTests; diff --git a/code-push-plugin-testing-framework/script/testBuilder.js b/code-push-plugin-testing-framework/script/testBuilder.js deleted file mode 100644 index 34a202952..000000000 --- a/code-push-plugin-testing-framework/script/testBuilder.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; -var ServerUtil = require("./serverUtil"); -var TestConfig = require("./testConfig"); -////////////////////////////////////////////////////////////////////////////////////////// -// Use this class to create and structure the tests. -// Usage is almost identical to Mocha, but with the addition of the optional "scenarioPath" in describe() and the required "isCoreTest" in it(). -var TestBuilder = (function () { - function TestBuilder() { - } - TestBuilder.describe = getDescribe(); - TestBuilder.it = getIt(); - return TestBuilder; -}()); -exports.TestBuilder = TestBuilder; -////////////////////////////////////////////////////////////////////////////////////////// -// Mocha mimicry -/** Singleton class for TestBuilder.describe to use internally to define the context. */ -var TestContext = (function () { - function TestContext() { - } - return TestContext; -}()); -exports.TestContext = TestContext; -function describeInternal(func, description, spec, scenarioPath) { - if (!TestContext.projectManager || !TestContext.targetPlatform) { - throw new Error("TestContext.projectManager or TestContext.targetPlatform are not defined! Did you call TestBuilder.describe outside of a function you passed to PluginTestingFramework.initializeTests?"); - } - return func(description, function () { - afterEach(function () { - console.log("Cleaning up!"); - ServerUtil.updateResponse = undefined; - ServerUtil.testMessageCallback = undefined; - ServerUtil.updateCheckCallback = undefined; - ServerUtil.testMessageResponse = undefined; - }); - beforeEach(function () { - return TestContext.targetPlatform.getEmulatorManager().prepareEmulatorForTest(TestConfig.TestNamespace) - .catch(function () { }); - }); - if (scenarioPath) { - before(function () { - return TestContext.projectManager.setupScenario(TestConfig.testRunDirectory, TestConfig.TestNamespace, TestConfig.templatePath, scenarioPath, TestContext.targetPlatform); - }); - } - spec(); - }); -} -/** - * Returns a hybrid type that mimics mocha's describe object. - */ -function getDescribe() { - var describer = function (description, spec, scenarioPath) { - describeInternal(describe, description, spec, scenarioPath); - }; - describer.only = function (description, spec, scenarioPath) { - describeInternal(describe.only, description, spec, scenarioPath); - }; - describer.skip = function (description, spec, scenarioPath) { - describeInternal(describe.skip, description, spec, scenarioPath); - }; - return describer; -} -function itInternal(func, expectation, isCoreTest, assertion) { - if ((!TestConfig.onlyRunCoreTests || isCoreTest)) { - // Create a wrapper around the assertion to set the timeout on the test to 10 minutes. - var assertionWithTimeout = function (done) { - this.timeout(10 * 2 * 60 * 1000); - assertion(done); - }; - return it(expectation, assertionWithTimeout); - } - return null; -} -/** - * Returns a hybrid type that mimics mocha's it object. - */ -function getIt() { - var itr = function (expectation, isCoreTest, assertion) { - itInternal(it, expectation, isCoreTest, assertion); - }; - itr.only = function (expectation, isCoreTest, assertion) { - itInternal(it.only, expectation, isCoreTest, assertion); - }; - itr.skip = function (expectation, isCoreTest, assertion) { - itInternal(it.skip, expectation, isCoreTest, assertion); - }; - return itr; -} diff --git a/code-push-plugin-testing-framework/script/testConfig.js b/code-push-plugin-testing-framework/script/testConfig.js deleted file mode 100644 index ba3651b76..000000000 --- a/code-push-plugin-testing-framework/script/testConfig.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; -// IMPORTS // -var os = require("os"); -var path = require("path"); -var TestUtil_1 = require("./testUtil"); -////////////////////////////////////////////////////////////////////////////////////////// -// Configuration variables. -// What plugin to use, what project directories to use, etc. -// COMMAND LINE OPTION NAMES, FLAGS, AND DEFAULTS -var DEFAULT_TEST_RUN_DIRECTORY = path.join(os.tmpdir(), TestUtil_1.TestUtil.getPluginName(), "test-run"); -var DEFAULT_UPDATES_DIRECTORY = path.join(os.tmpdir(), TestUtil_1.TestUtil.getPluginName(), "updates"); -var DEFAULT_PLUGIN_PATH = path.join(__dirname, "../.."); -var NPM_PLUGIN_PATH = TestUtil_1.TestUtil.getPluginName(); -var SETUP_FLAG_NAME = "--setup"; -var DEFAULT_PLUGIN_TGZ_NAME = TestUtil_1.TestUtil.getPluginName() + "-" + TestUtil_1.TestUtil.getPluginVersion() + ".tgz"; -// CONST VARIABLES -exports.TestAppName = "TestCodePush"; -exports.TestNamespace = "com.testcodepush"; -exports.AcquisitionSDKPluginName = "code-push"; -exports.templatePath = path.join(__dirname, "../../test/template"); -exports.thisPluginInstallString = TestUtil_1.TestUtil.resolveBooleanVariables(process.env.NPM) ? `npm install ${NPM_PLUGIN_PATH}` : `npm pack ${DEFAULT_PLUGIN_PATH} && npm install ${DEFAULT_PLUGIN_TGZ_NAME} && npm link`; -exports.testRunDirectory = process.env.RUN_DIR ? process.env.RUN_DIR: DEFAULT_TEST_RUN_DIRECTORY; -exports.updatesDirectory = process.env.UPDATE_DIR ? process.env.UPDATE_DIR : DEFAULT_UPDATES_DIRECTORY; -exports.onlyRunCoreTests = TestUtil_1.TestUtil.resolveBooleanVariables(process.env.CORE); -exports.shouldSetup = TestUtil_1.TestUtil.readMochaCommandLineFlag(SETUP_FLAG_NAME); -exports.restartEmulators = TestUtil_1.TestUtil.resolveBooleanVariables(process.env.CLEAN); diff --git a/code-push-plugin-testing-framework/script/testUtil.js b/code-push-plugin-testing-framework/script/testUtil.js deleted file mode 100644 index 5d39f503f..000000000 --- a/code-push-plugin-testing-framework/script/testUtil.js +++ /dev/null @@ -1,165 +0,0 @@ -"use strict"; -var archiver = require("archiver"); -var child_process = require("child_process"); -var fs = require("fs"); -var replace = require("replace"); -var Q = require("q"); -var TestUtil = (function () { - function TestUtil() { - } - //// Command Line Input Functions - /** - * Reads a command line option passed to mocha and returns a default if unspecified. - */ - TestUtil.readMochaCommandLineOption = function (optionName, defaultValue) { - var optionValue = undefined; - for (var i = 0; i < process.argv.length; i++) { - if (process.argv[i] === optionName) { - if (i + 1 < process.argv.length) { - optionValue = process.argv[i + 1]; - } - break; - } - } - if (!optionValue) - optionValue = defaultValue; - return optionValue; - }; - /** - * Reads command line options passed to mocha. - */ - TestUtil.readMochaCommandLineFlag = function (optionName) { - for (var i = 0; i < process.argv.length; i++) { - if (process.argv[i] === optionName) { - return true; - } - } - return false; - }; - //// Utility Functions - /** - * Executes a child process and returns a promise that resolves with its output or rejects with its error. - */ - TestUtil.getProcessOutput = function (command, options) { - var deferred = Q.defer(); - options = options || {}; - // set default options - if (options.maxBuffer === undefined) - options.maxBuffer = 1024 * 1024 * 500; - if (options.timeout === undefined) - options.timeout = 10 * 60 * 1000; - if (!options.noLogCommand) - console.log("Running command: " + command); - var execProcess = child_process.exec(command, options, function (error, stdout, stderr) { - if (error) { - if (!options.noLogStdErr) - console.error("" + error); - deferred.reject(error); - } - else { - deferred.resolve(stdout.toString()); - } - }); - if (!options.noLogStdOut) - execProcess.stdout.pipe(process.stdout); - if (!options.noLogStdErr) - execProcess.stderr.pipe(process.stderr); - execProcess.on('error', function (error) { - if (!options.noLogStdErr) - console.error("" + error); - deferred.reject(error); - }); - return deferred.promise; - }; - /** - * Returns the name of the plugin that is being tested. - */ - TestUtil.getPluginName = function () { - var packageFile = JSON.parse(fs.readFileSync("./package.json", "utf8")); - return packageFile.name; - }; - - TestUtil.getPluginVersion = function () { - var packageFile = JSON.parse(fs.readFileSync("./package.json", "utf8")); - return packageFile.version; - }; - /** - * Replaces a regex in a file with a given string. - */ - TestUtil.replaceString = function (filePath, regex, replacement) { - console.log("replacing \"" + regex + "\" with \"" + replacement + "\" in " + filePath); - replace({ regex: regex, replacement: replacement, recursive: false, silent: true, paths: [filePath] }); - }; - /** - * Copies a file from a given location to another. - */ - TestUtil.copyFile = function (source, destination, overwrite) { - var deferred = Q.defer(); - try { - var errorHandler = function (error) { - deferred.reject(error); - }; - if (overwrite && fs.existsSync(destination)) { - fs.unlinkSync(destination); - } - var readStream = fs.createReadStream(source); - readStream.on("error", errorHandler); - var writeStream = fs.createWriteStream(destination); - writeStream.on("error", errorHandler); - writeStream.on("close", deferred.resolve.bind(undefined, undefined)); - readStream.pipe(writeStream); - } - catch (e) { - deferred.reject(e); - } - return deferred.promise; - }; - /** - * Archives the contents of sourceFolder and puts it in an archive at archivePath in targetFolder. - */ - TestUtil.archiveFolder = function (sourceFolder, targetFolder, archivePath, isDiff) { - var deferred = Q.defer(); - var archive = archiver.create("zip", {}); - console.log("Creating an update archive at: " + archivePath); - if (fs.existsSync(archivePath)) { - fs.unlinkSync(archivePath); - } - var writeStream = fs.createWriteStream(archivePath); - writeStream.on("close", function () { - deferred.resolve(archivePath); - }); - archive.on("error", function (e) { - deferred.reject(e); - }); - if (isDiff) { - archive.append("{\"deletedFiles\":[]}", { name: "hotcodepush.json" }); - } - archive.directory(sourceFolder, targetFolder); - archive.pipe(writeStream); - archive.finalize(); - return deferred.promise; - }; - - /** - * Check that boolean environment variable string is 'true. - */ - TestUtil.resolveBooleanVariables = function(variable) { - if (variable) { - return variable.toLowerCase() === 'true'; - } - - return false; - } - //// Placeholders - // Used in the template to represent data that needs to be added by the testing framework at runtime. - TestUtil.ANDROID_KEY_PLACEHOLDER = "CODE_PUSH_ANDROID_DEPLOYMENT_KEY"; - TestUtil.IOS_KEY_PLACEHOLDER = "CODE_PUSH_IOS_DEPLOYMENT_KEY"; - TestUtil.SERVER_URL_PLACEHOLDER = "CODE_PUSH_SERVER_URL"; - TestUtil.INDEX_JS_PLACEHOLDER = "CODE_PUSH_INDEX_JS_PATH"; - TestUtil.CODE_PUSH_APP_VERSION_PLACEHOLDER = "CODE_PUSH_APP_VERSION"; - TestUtil.CODE_PUSH_TEST_APP_NAME_PLACEHOLDER = "CODE_PUSH_TEST_APP_NAME"; - TestUtil.CODE_PUSH_APP_ID_PLACEHOLDER = "CODE_PUSH_TEST_APPLICATION_ID"; - TestUtil.PLUGIN_VERSION_PLACEHOLDER = "CODE_PUSH_PLUGIN_VERSION"; - return TestUtil; -}()); -exports.TestUtil = TestUtil; diff --git a/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts b/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts deleted file mode 100644 index 92dd20e92..000000000 --- a/code-push-plugin-testing-framework/typings/code-push-plugin-testing-framework.d.ts +++ /dev/null @@ -1,487 +0,0 @@ -declare module 'code-push-plugin-testing-framework/script/platform' { - import Q = require("q"); - /** - * Defines a platform supported by CodePush. - */ - export interface IPlatform { - /** - * Gets the platform name. (e.g. "android" for the Android platform). - */ - getName(): string; - /** - * The command line flag used to determine whether or not this platform should run. - * Runs when the flag is present, doesn't run otherwise. - */ - getCommandLineFlagName(): string; - /** - * Gets the server url used for testing. - */ - getServerUrl(): string; - /** - * Gets an IEmulatorManager that is used to control the emulator during the tests. - */ - getEmulatorManager(): IEmulatorManager; - /** - * Gets the default deployment key. - */ - getDefaultDeploymentKey(): string; - } - /** - * Manages the interaction with the emulator. - */ - export interface IEmulatorManager { - /** - * Returns the target emulator, which is specified through the command line. - */ - getTargetEmulator(): Q.Promise; - /** - * Boots the target emulator. - */ - bootEmulator(restartEmulators: boolean): Q.Promise; - /** - * Launches an already installed application by app id. - */ - launchInstalledApplication(appId: string): Q.Promise; - /** - * Ends a running application given its app id. - */ - endRunningApplication(appId: string): Q.Promise; - /** - * Restarts an already installed application by app id. - */ - restartApplication(appId: string): Q.Promise; - /** - * 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; - /** - * Prepares the emulator for a test. - */ - prepareEmulatorForTest(appId: string): Q.Promise; - /** - * Uninstalls the app from the emulator. - */ - uninstallApplication(appId: string): Q.Promise; - } - /** - * Android implementations of IPlatform. - */ - export class Android implements IPlatform { - private emulatorManager; - private serverUrl; - constructor(emulatorManager: IEmulatorManager); - /** - * Gets the platform name. (e.g. "android" for the Android platform). - */ - getName(): string; - /** - * The command line flag used to determine whether or not this platform should run. - * Runs when the flag is present, doesn't run otherwise. - */ - getCommandLineFlagName(): string; - private static DEFAULT_ANDROID_SERVER_URL; - /** - * Gets the server url used for testing. - */ - getServerUrl(): string; - /** - * Gets an IEmulatorManager that is used to control the emulator during the tests. - */ - getEmulatorManager(): IEmulatorManager; - /** - * Gets the default deployment key. - */ - getDefaultDeploymentKey(): string; - } - /** - * IOS implementation of IPlatform. - */ - export class IOS implements IPlatform { - private emulatorManager; - private serverUrl; - constructor(emulatorManager: IEmulatorManager); - /** - * Gets the platform name. (e.g. "android" for the Android platform). - */ - getName(): string; - /** - * The command line flag used to determine whether or not this platform should run. - * Runs when the flag is present, doesn't run otherwise. - */ - getCommandLineFlagName(): string; - private static DEFAULT_IOS_SERVER_URL; - /** - * Gets the server url used for testing. - */ - getServerUrl(): string; - /** - * Gets an IEmulatorManager that is used to control the emulator during the tests. - */ - getEmulatorManager(): IEmulatorManager; - /** - * Gets the default deployment key. - */ - getDefaultDeploymentKey(): string; - } - export class AndroidEmulatorManager implements IEmulatorManager { - private targetEmulator; - /** - * Returns the target emulator, which is specified through the command line. - */ - getTargetEmulator(): Q.Promise; - /** - * Boots the target emulator. - */ - bootEmulator(restartEmulators: boolean): Q.Promise; - /** - * Launches an already installed application by app id. - */ - launchInstalledApplication(appId: string): Q.Promise; - /** - * Ends a running application given its app id. - */ - endRunningApplication(appId: string): Q.Promise; - /** - * Restarts an already installed application by app id. - */ - restartApplication(appId: string): Q.Promise; - /** - * 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; - /** - * Prepares the emulator for a test. - */ - prepareEmulatorForTest(appId: string): Q.Promise; - /** - * Uninstalls the app from the emulator. - */ - uninstallApplication(appId: string): Q.Promise; - } - export class IOSEmulatorManager implements IEmulatorManager { - private targetEmulator; - /** - * Returns the target emulator, which is specified through the command line. - */ - getTargetEmulator(): Q.Promise; - /** - * Boots the target emulator. - */ - bootEmulator(restartEmulators: boolean): Q.Promise; - /** - * Launches an already installed application by app id. - */ - launchInstalledApplication(appId: string): Q.Promise; - /** - * Ends a running application given its app id. - */ - endRunningApplication(appId: string): Q.Promise; - /** - * Restarts an already installed application by app id. - */ - restartApplication(appId: string): Q.Promise; - /** - * 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; - /** - * Prepares the emulator for a test. - */ - prepareEmulatorForTest(appId: string): Q.Promise; - /** - * Uninstalls the app from the emulator. - */ - uninstallApplication(appId: string): Q.Promise; - } - -} -declare module 'code-push-plugin-testing-framework/script/projectManager' { - import Q = require("q"); - import platform = require('code-push-plugin-testing-framework/script/platform'); - /** - * In charge of project related operations. - */ - export class ProjectManager { - static DEFAULT_APP_VERSION: string; - private static NOT_IMPLEMENTED_ERROR_MSG; - /** - * Returns the name of the plugin being tested, for example Cordova or React-Native. - * - * Overwrite this in your implementation! - */ - getPluginName(): string; - /** - * Creates a new test application at the specified path, and configures it - * with the given server URL, android and ios deployment keys. - * - * Overwrite this in your implementation! - */ - setupProject(projectDirectory: string, templatePath: string, appName: string, appNamespace: string, version?: string): Q.Promise; - /** - * Sets up the scenario for a test in an already existing project. - * - * Overwrite this in your implementation! - */ - setupScenario(projectDirectory: string, appId: string, templatePath: string, jsPath: string, targetPlatform: platform.IPlatform, version?: string): Q.Promise; - /** - * Creates a CodePush update package zip for a project. - * - * Overwrite this in your implementation! - */ - createUpdateArchive(projectDirectory: string, targetPlatform: platform.IPlatform, isDiff?: boolean): Q.Promise; - /** - * Prepares a specific platform for tests. - * - * Overwrite this in your implementation! - */ - preparePlatform(projectDirectory: string, targetPlatform: platform.IPlatform): Q.Promise; - /** - * Cleans up a specific platform after tests. - * - * Overwrite this in your implementation! - */ - cleanupAfterPlatform(projectDirectory: string, targetPlatform: platform.IPlatform): Q.Promise; - /** - * Runs the test app on the given target / platform. - * - * Overwrite this in your implementation! - */ - runApplication(projectDirectory: string, targetPlatform: platform.IPlatform): Q.Promise; - } - /** - * Wrapper for ProjectManager.setupScenario in the TestRun directory. - */ - export function setupTestRunScenario(projectManager: ProjectManager, targetPlatform: platform.IPlatform, scenarioJsPath: string, version?: string): Q.Promise; - /** - * Creates an update and zip for the test app using the specified scenario and version. - */ - export function setupUpdateScenario(projectManager: ProjectManager, targetPlatform: platform.IPlatform, scenarioJsPath: string, version: string): Q.Promise; - -} -declare module 'code-push-plugin-testing-framework/script/test' { - import Platform = require('code-push-plugin-testing-framework/script/platform'); - import { ProjectManager } from 'code-push-plugin-testing-framework/script/projectManager'; - /** - * Call this function to initialize the automated tests. - */ - export function initializeTests(projectManager: ProjectManager, supportedTargetPlatforms: Platform.IPlatform[], describeTests: (projectManager: ProjectManager, targetPlatform: Platform.IPlatform) => void): void; - -} -declare module 'code-push-plugin-testing-framework/script/serverUtil' { - import platform = require('code-push-plugin-testing-framework/script/platform'); - import Q = require("q"); - /** The server to respond to requests from the app. */ - export var server: any; - /** Response the server gives the next update check request */ - export var updateResponse: any; - /** Response the server gives the next test message request */ - export var testMessageResponse: any; - /** Called after the next test message request */ - export var testMessageCallback: (requestBody: any) => void; - /** Called after the next update check request */ - export var updateCheckCallback: (requestBody: any) => void; - /** Location of the update package given in the update check response */ - export var updatePackagePath: string; - /** - * Sets up the server that the test app uses to send test messages and check for and download updates. - */ - export function setupServer(targetPlatform: platform.IPlatform): void; - /** - * Closes the server. - */ - export function cleanupServer(): void; - /** - * Class used to mock the codePush.checkForUpdate() response from the server. - */ - export class CheckForUpdateResponseMock { - download_url: string; - is_available: boolean; - should_run_binary_version: boolean; - package_size: number; - update_app_version: boolean; - target_binary_range: string; - is_disabled: boolean; - description: string; - label: string; - package_hash: string; - is_mandatory: boolean; - } - /** - * The model class of the codePush.checkForUpdate() request to the server. - */ - export class UpdateCheckRequestMock { - deploymentKey: string; - appVersion: string; - packageHash: string; - isCompanion: boolean; - } - /** - * Returns a default empty response to give to the app in a checkForUpdate request - */ - export function createDefaultResponse(): CheckForUpdateResponseMock; - /** - * Returns a default update response to give to the app in a checkForUpdate request - */ - export function createUpdateResponse(mandatory?: boolean, targetPlatform?: platform.IPlatform, randomHash?: boolean): CheckForUpdateResponseMock; - /** - * Returns a promise that waits for the next set of test messages sent by the app and resolves if that they are equal to the expected messages or rejects if they are not. - */ - export function expectTestMessages(expectedMessages: (string | AppMessage)[]): Q.Promise; - /** - * Contains all the messages sent from the application to the mock server during tests. - */ - export class TestMessage { - static CHECK_UP_TO_DATE: string; - static CHECK_UPDATE_AVAILABLE: string; - static CHECK_ERROR: string; - static DOWNLOAD_SUCCEEDED: string; - static DOWNLOAD_ERROR: string; - static UPDATE_INSTALLED: string; - static INSTALL_ERROR: string; - static DEVICE_READY_AFTER_UPDATE: string; - static UPDATE_FAILED_PREVIOUSLY: string; - static NOTIFY_APP_READY_SUCCESS: string; - static NOTIFY_APP_READY_FAILURE: string; - static SKIPPED_NOTIFY_APPLICATION_READY: string; - static SYNC_STATUS: string; - static RESTART_SUCCEEDED: string; - static RESTART_FAILED: string; - static PENDING_PACKAGE: string; - static CURRENT_PACKAGE: string; - static SYNC_UP_TO_DATE: number; - static SYNC_UPDATE_INSTALLED: number; - static SYNC_UPDATE_IGNORED: number; - static SYNC_ERROR: number; - static SYNC_IN_PROGRESS: number; - static SYNC_CHECKING_FOR_UPDATE: number; - static SYNC_AWAITING_USER_ACTION: number; - static SYNC_DOWNLOADING_PACKAGE: number; - static SYNC_INSTALLING_UPDATE: number; - } - /** - * Contains all the messages sent from the mock server back to the application during tests. - */ - export class TestMessageResponse { - static SKIP_NOTIFY_APPLICATION_READY: string; - } - /** - * Defines the messages sent from the application to the mock server during tests. - */ - export class AppMessage { - message: string; - args: any[]; - constructor(message: string, args: any[]); - static fromString(message: string): AppMessage; - } - /** - * Checks if two messages are equal. - */ - export function areEqual(m1: AppMessage, m2: AppMessage): boolean; - -} -declare module 'code-push-plugin-testing-framework/script/testBuilder' { - import Platform = require('code-push-plugin-testing-framework/script/platform'); - import { ProjectManager } from 'code-push-plugin-testing-framework/script/projectManager'; - export class TestBuilder { - static describe: ITestBuilderContextDefintion; - static it: ITestBuilderTestDefinition; - } - /** Singleton class for TestBuilder.describe to use internally to define the context. */ - export class TestContext { - static projectManager: ProjectManager; - static targetPlatform: Platform.IPlatform; - } - export interface ITestBuilderContextDefintion { - (description: string, spec: () => void, scenarioPath?: string): void; - only(description: string, spec: () => void, scenarioPath?: string): void; - skip(description: string, spec: () => void, scenarioPath?: string): void; - } - export interface ITestBuilderTestDefinition { - (expectation: string, isCoreTest: boolean, assertion: (done: Mocha.Done) => void): void; - only(expectation: string, isCoreTest: boolean, assertion: (done: Mocha.Done) => void): void; - skip(expectation: string, isCoreTest: boolean, assertion: (done: Mocha.Done) => void): void; - } - -} -declare module 'code-push-plugin-testing-framework/script/testConfig' { - export const TestAppName: string; - export const TestNamespace: string; - export const AcquisitionSDKPluginName: string; - export const templatePath: string; - export const thisPluginInstallString: string; - export const testRunDirectory: string; - export const updatesDirectory: string; - export const onlyRunCoreTests: boolean; - export const shouldSetup: boolean; - export const restartEmulators: boolean; - -} -declare module 'code-push-plugin-testing-framework/script/testUtil' { - import Q = require("q"); - export class TestUtil { - static ANDROID_KEY_PLACEHOLDER: string; - static IOS_KEY_PLACEHOLDER: string; - static SERVER_URL_PLACEHOLDER: string; - static INDEX_JS_PLACEHOLDER: string; - static CODE_PUSH_APP_VERSION_PLACEHOLDER: string; - static CODE_PUSH_TEST_APP_NAME_PLACEHOLDER: string; - static CODE_PUSH_APP_ID_PLACEHOLDER: string; - static PLUGIN_VERSION_PLACEHOLDER: string; - /** - * Reads a command line option passed to mocha and returns a default if unspecified. - */ - static readMochaCommandLineOption(optionName: string, defaultValue?: string): string; - /** - * Reads command line options passed to mocha. - */ - static readMochaCommandLineFlag(optionName: string): boolean; - /** - * Executes a child process and returns a promise that resolves with its output or rejects with its error. - */ - static getProcessOutput(command: string, options?: { - cwd?: string; - stdio?: any; - customFds?: any; - env?: any; - encoding?: string; - timeout?: number; - maxBuffer?: number; - killSignal?: string; - noLogCommand?: boolean; - noLogStdOut?: boolean; - noLogStdErr?: boolean; - }): Q.Promise; - /** - * Returns the name of the plugin that is being tested. - */ - static getPluginName(): string; - /** - * Replaces a regex in a file with a given string. - */ - static replaceString(filePath: string, regex: string, replacement: string): void; - /** - * Copies a file from a given location to another. - */ - static copyFile(source: string, destination: string, overwrite: boolean): Q.Promise; - /** - * Archives the contents of sourceFolder and puts it in an archive at archivePath in targetFolder. - */ - static archiveFolder(sourceFolder: string, targetFolder: string, archivePath: string, isDiff: boolean): Q.Promise; - } - -} -declare module 'code-push-plugin-testing-framework/script/index' { - import * as Platform from 'code-push-plugin-testing-framework/script/platform'; - import * as PluginTestingFramework from 'code-push-plugin-testing-framework/script/test'; - import { ProjectManager, setupTestRunScenario, setupUpdateScenario } from 'code-push-plugin-testing-framework/script/projectManager'; - import * as ServerUtil from 'code-push-plugin-testing-framework/script/serverUtil'; - import { TestBuilder } from 'code-push-plugin-testing-framework/script/testBuilder'; - import * as TestConfig from 'code-push-plugin-testing-framework/script/testConfig'; - import { TestUtil } from 'code-push-plugin-testing-framework/script/testUtil'; - export { Platform, PluginTestingFramework, ProjectManager, setupTestRunScenario, setupUpdateScenario, ServerUtil, TestBuilder, TestConfig, TestUtil }; - -} -declare module 'code-push-plugin-testing-framework' { - import main = require('code-push-plugin-testing-framework/script/index'); - export = main; -} diff --git a/e2e/README.ko.md b/e2e/README.ko.md new file mode 100644 index 000000000..309db519e --- /dev/null +++ b/e2e/README.ko.md @@ -0,0 +1,96 @@ +# E2E 테스트 실행 가이드 + +[Maestro](https://maestro.mobile.dev/)를 사용한 `react-native-code-push` E2E 테스트입니다. + +## 사전 요구사항 + +- **Node.js** (v18 이상) +- **Maestro CLI** — [설치 가이드](https://maestro.mobile.dev/getting-started/installing-maestro) +- **iOS**: Xcode 및 부팅된 iOS 시뮬레이터 +- **Android**: Android SDK 및 실행 중인 에뮬레이터 +- `Examples/` 디렉토리에 설정된 예제 앱 (예: `RN0840`) + +## 빠른 시작 + +```bash +# 전체 실행 (빌드 + 테스트) +npm run e2e -- --app RN0840 --platform ios + +# 빌드 생략, Maestro 플로우만 실행 +npm run e2e -- --app RN0840 --platform ios --maestro-only +``` + +## CLI 옵션 + +| 옵션 | 필수 | 설명 | +|---|---|---| +| `--app ` | 예 | 예제 앱 디렉토리 이름 (예: `RN0840`) | +| `--platform ` | 예 | `ios` 또는 `android` | +| `--simulator ` | 아니오 | iOS 시뮬레이터 이름 (부팅된 시뮬레이터 자동 감지, 기본값 "iPhone 16") | +| `--maestro-only` | 아니오 | 빌드 단계 생략, Maestro 플로우만 실행 | + +## 실행 과정 + +테스트 러너(`e2e/run.ts`)는 다음 단계를 순서대로 실행합니다: + +### Phase 1 — 기본 플로우 (`flows/`) + +1. **설정 준비** — `App.tsx`를 로컬 mock 서버를 가리키도록 패치하고, `code-push.config.local.ts`를 앱 디렉토리에 복사합니다. +2. **앱 빌드** — 예제 앱을 Release 모드로 빌드하여 시뮬레이터/에뮬레이터에 설치합니다. +3. **번들 준비** — `npx code-push release`로 릴리스 히스토리를 생성하고 v1.0.1을 번들링합니다. +4. **Mock 서버 시작** — 번들과 릴리스 히스토리 JSON을 서빙하는 로컬 HTTP 서버(포트 18081)를 시작합니다. +5. **Maestro 플로우 실행**: + - `01-app-launch` — 앱 실행 및 UI 요소 존재 확인 + - `02-restart-no-crash` — 재시작 탭 후 크래시 없음 확인 + - `03-update-flow` — 이전 업데이트 초기화, sync 트리거, 업데이트 설치 확인("UPDATED!" 표시) 및 메타데이터 `METADATA_V1.0.1` 확인 + +### Phase 2 — 바이너리로 롤백 (`flows-rollback/`) + +6. **릴리스 비활성화** — `npx code-push update-history -e false`로 v1.0.1을 비활성화합니다. +7. **롤백 플로우 실행** — `01-rollback`: 업데이트가 설치된 상태에서 앱을 실행하고 sync를 트리거합니다. 라이브러리가 비활성화된 릴리스를 감지하여 자동으로 바이너리 버전으로 롤백합니다. + +### Phase 3 — 부분 롤백 (`flows-partial-rollback/`) + +8. **두 개의 릴리스 준비** — 릴리스 마커를 사용하여 서로 다른 해시를 가진 v1.0.1과 v1.0.2를 번들링합니다. +9. **최신 버전으로 업데이트** — `01-update-to-latest`: 바이너리에서 시작하여 v1.0.2로 sync, `METADATA_V1.0.2` 확인 +10. **v1.0.2만 비활성화** — `npx code-push update-history`로 v1.0.2만 비활성화합니다. +11. **이전 업데이트로 롤백** — `02-rollback-to-previous`: v1.0.2에서 v1.0.1로 롤백되는 것을 확인합니다 (바이너리가 아닌 이전 업데이트로). + +## 아키텍처 + +``` +e2e/ +├── run.ts # 메인 오케스트레이션 스크립트 +├── config.ts # 경로, 포트, 호스트 설정 +├── tsconfig.json +├── mock-server/ +│ └── server.ts # Express 정적 파일 서버 (포트 18081) +├── templates/ +│ └── code-push.config.local.ts # 파일시스템 기반 CodePush 설정 +├── helpers/ +│ ├── prepare-config.ts # App.tsx 패치, 설정 복사 +│ ├── prepare-bundle.ts # code-push CLI로 번들 생성 +│ └── build-app.ts # iOS/Android Release 빌드 +├── flows/ # Phase 1: 기본 플로우 +├── flows-rollback/ # Phase 2: 바이너리로 롤백 +└── flows-partial-rollback/ # Phase 3: 부분 롤백 (v1.0.2 → v1.0.1) +``` + +### Mock 서버 + +실제 CodePush 서버 대신, 로컬 Express 서버가 다음을 서빙합니다: +- **번들**: `mock-server/data/bundles/{platform}/{identifier}/` +- **릴리스 히스토리**: `mock-server/data/histories/{platform}/{identifier}/{version}.json` + +`code-push.config.local.ts` 템플릿은 모든 CLI 작업(업로드, 히스토리 읽기/쓰기)을 로컬 파일시스템으로 라우팅하며, 앱의 `CODEPUSH_HOST`는 mock 서버를 가리키도록 패치됩니다. + +### 릴리스 마커 + +동일한 소스 코드로 여러 릴리스(예: v1.0.1과 v1.0.2)를 생성하면 번들 JavaScript의 해시가 동일해져 CodePush가 같은 업데이트로 인식합니다. 이를 방지하기 위해 러너는 각 릴리스 전에 `App.tsx`에 `console.log("E2E_MARKER_{version}")`를 주입합니다. 이 코드는 미니피케이션 후에도 유지되어 고유한 번들 해시를 생성합니다. + +## 문제 해결 + +- **iOS 빌드 시 서명 오류**: setup 스크립트가 `SUPPORTED_PLATFORMS = iphonesimulator`를 설정하고 코드 서명을 비활성화합니다. `scripts/setupExampleApp`으로 예제 앱이 설정되었는지 확인하세요. +- **Maestro가 앱을 찾지 못함**: 실행 전에 시뮬레이터/에뮬레이터가 부팅되어 있는지 확인하세요. iOS의 경우 스크립트가 부팅된 시뮬레이터를 자동 감지합니다. +- **Android 네트워크 오류**: Android 에뮬레이터는 호스트 머신의 localhost에 접근하기 위해 `10.0.2.2`를 사용합니다. 설정에서 자동으로 처리됩니다. +- **업데이트가 적용되지 않음**: Mock 서버가 실행 중인지(포트 18081), `mock-server/data/`에 예상되는 번들과 히스토리 파일이 있는지 확인하세요. diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 000000000..4e84eb111 --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,96 @@ +# E2E Testing Guide + +End-to-end tests for `react-native-code-push` using [Maestro](https://maestro.mobile.dev/). + +## Prerequisites + +- **Node.js** (v18+) +- **Maestro CLI** — [Install guide](https://maestro.mobile.dev/getting-started/installing-maestro) +- **iOS**: Xcode with a booted iOS Simulator +- **Android**: Android SDK with a running emulator +- An example app set up under `Examples/` (e.g. `RN0840`) + +## Quick Start + +```bash +# Full run (build + test) +npm run e2e -- --app RN0840 --platform ios + +# Skip build, run Maestro flows only +npm run e2e -- --app RN0840 --platform ios --maestro-only +``` + +## CLI Options + +| Option | Required | Description | +|---|---|---| +| `--app ` | Yes | Example app directory name (e.g. `RN0840`) | +| `--platform ` | Yes | `ios` or `android` | +| `--simulator ` | No | iOS simulator name (auto-detects booted simulator, defaults to "iPhone 16") | +| `--maestro-only` | No | Skip build step, only run Maestro flows | + +## What It Does + +The test runner (`e2e/run.ts`) executes these phases in order: + +### Phase 1 — Basic Flows (`flows/`) + +1. **Prepare config** — Patches `App.tsx` to point at a local mock server, copies `code-push.config.local.ts` to the app directory. +2. **Build app** — Builds the example app in Release mode and installs it on the simulator/emulator. +3. **Prepare bundle** — Creates release history and bundles v1.0.1 using `npx code-push release`. +4. **Start mock server** — Starts a local HTTP server (port 18081) that serves bundles and release history JSON. +5. **Run Maestro flows** — Executes: + - `01-app-launch` — Verifies the app launches and UI elements are present. + - `02-restart-no-crash` — Taps Restart, confirms app doesn't crash. + - `03-update-flow` — Clears any previous update, triggers sync, verifies update installs (shows "UPDATED!") and metadata shows `METADATA_V1.0.1`. + +### Phase 2 — Rollback to Binary (`flows-rollback/`) + +6. **Disable release** — Disables v1.0.1 via `npx code-push update-history -e false`. +7. **Run rollback flow** — `01-rollback`: Launches app with the update installed, triggers sync. The library detects the disabled release and automatically rolls back to the binary version. + +### Phase 3 — Partial Rollback (`flows-partial-rollback/`) + +8. **Prepare two releases** — Bundles v1.0.1 and v1.0.2 with different content (using release markers for unique hashes). +9. **Update to latest** — `01-update-to-latest`: Starts from binary, syncs to v1.0.2, verifies `METADATA_V1.0.2`. +10. **Disable v1.0.2 only** — Disables only v1.0.2 via `npx code-push update-history`. +11. **Rollback to previous update** — `02-rollback-to-previous`: Verifies the app rolls back from v1.0.2 to v1.0.1 (not to the binary). + +## Architecture + +``` +e2e/ +├── run.ts # Main orchestration script +├── config.ts # Paths, ports, host configuration +├── tsconfig.json +├── mock-server/ +│ └── server.ts # Express static file server (port 18081) +├── templates/ +│ └── code-push.config.local.ts # Filesystem-based CodePush config +├── helpers/ +│ ├── prepare-config.ts # Patches App.tsx, copies config +│ ├── prepare-bundle.ts # Runs code-push CLI to create bundles +│ └── build-app.ts # Builds iOS/Android in Release mode +├── flows/ # Phase 1: basic flows +├── flows-rollback/ # Phase 2: rollback to binary +└── flows-partial-rollback/ # Phase 3: partial rollback (v1.0.2 → v1.0.1) +``` + +### Mock Server + +Instead of a real CodePush server, tests use a local Express server that serves: +- **Bundles**: `mock-server/data/bundles/{platform}/{identifier}/` +- **Release history**: `mock-server/data/histories/{platform}/{identifier}/{version}.json` + +The `code-push.config.local.ts` template routes all CLI operations (upload, history read/write) to this local filesystem, and the app's `CODEPUSH_HOST` is patched to point at the mock server. + +### Release Markers + +When creating multiple releases with identical source code (e.g. v1.0.1 and v1.0.2), the bundled JavaScript would produce the same hash, causing CodePush to treat them as the same update. To avoid this, the runner injects `console.log("E2E_MARKER_{version}")` into `App.tsx` before each release, which survives minification and produces unique bundle hashes. + +## Troubleshooting + +- **Build fails with signing error (iOS)**: The setup script sets `SUPPORTED_PLATFORMS = iphonesimulator` and disables code signing. Make sure the example app was set up with `scripts/setupExampleApp`. +- **Maestro can't find the app**: Ensure the simulator/emulator is booted before running. For iOS, the script auto-detects the booted simulator. +- **Android network error**: Android emulators use `10.0.2.2` to reach the host machine's localhost. This is handled automatically by the config. +- **Update not applying**: Check that the mock server is running (port 18081) and that `mock-server/data/` contains the expected bundle and history files. diff --git a/e2e/config.ts b/e2e/config.ts new file mode 100644 index 000000000..adf450121 --- /dev/null +++ b/e2e/config.ts @@ -0,0 +1,14 @@ +import path from "path"; + +export const MOCK_SERVER_PORT = 18081; +export const EXAMPLES_DIR = path.resolve(__dirname, "../Examples"); +export const MOCK_DATA_DIR = path.resolve(__dirname, "mock-server/data"); + +export function getMockServerHost(platform: "ios" | "android"): string { + const host = platform === "android" ? "10.0.2.2" : "localhost"; + return `http://${host}:${MOCK_SERVER_PORT}`; +} + +export function getAppPath(appName: string): string { + return path.join(EXAMPLES_DIR, appName); +} \ No newline at end of file diff --git a/e2e/flows-partial-rollback/01-update-to-latest.yaml b/e2e/flows-partial-rollback/01-update-to-latest.yaml new file mode 100644 index 000000000..0616a3d35 --- /dev/null +++ b/e2e/flows-partial-rollback/01-update-to-latest.yaml @@ -0,0 +1,18 @@ +appId: ${APP_ID} +--- +# App should be on binary (after phase 2 rollback) +- launchApp +- assertVisible: "React Native.*" +- assertNotVisible: "UPDATED!" + +# Sync — should update to 1.0.2 (latest enabled) +- tapOn: "Check for updates" +- waitForAnimationToEnd: + timeout: 30000 +- assertVisible: "UPDATED!" + +# Verify running version is 1.0.2 +- tapOn: "Get update metadata" +- waitForAnimationToEnd: + timeout: 3000 +- assertVisible: "METADATA_V1.0.2" diff --git a/e2e/flows-partial-rollback/02-rollback-to-previous.yaml b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml new file mode 100644 index 000000000..db910b006 --- /dev/null +++ b/e2e/flows-partial-rollback/02-rollback-to-previous.yaml @@ -0,0 +1,24 @@ +appId: ${APP_ID} +--- +# App should be on 1.0.2 (from previous update) +- launchApp +- assertVisible: "UPDATED!" +- tapOn: "Get update metadata" +- waitForAnimationToEnd: + timeout: 3000 +- assertVisible: "METADATA_V1.0.2" + +# Sync — 1.0.2 is disabled, triggers rollback/update to 1.0.1 +- tapOn: "Check for updates" +- waitForAnimationToEnd: + timeout: 30000 + +# After rollback to 1.0.1, UPDATED! should still be visible +- assertVisible: "UPDATED!" +- assertVisible: "React Native.*" + +# Verify running version is 1.0.1 (not 1.0.0 binary) +- tapOn: "Get update metadata" +- waitForAnimationToEnd: + timeout: 3000 +- assertVisible: "METADATA_V1.0.1" diff --git a/e2e/flows-rollback/01-rollback.yaml b/e2e/flows-rollback/01-rollback.yaml new file mode 100644 index 000000000..8f6a56bee --- /dev/null +++ b/e2e/flows-rollback/01-rollback.yaml @@ -0,0 +1,16 @@ +appId: ${APP_ID} +--- +# App should have the update installed from phase 1 +- launchApp +- assertVisible: "UPDATED!" + +# Check for updates — server has disabled the release, triggers rollback to binary +- tapOn: "Check for updates" + +# sync() detects disabled release, calls clearUpdates() + restartApp() automatically +- waitForAnimationToEnd: + timeout: 30000 + +# After rollback, UPDATED! should be gone +- assertVisible: "React Native.*" +- assertNotVisible: "UPDATED!" diff --git a/e2e/flows/01-app-launch.yaml b/e2e/flows/01-app-launch.yaml new file mode 100644 index 000000000..8ca6a7cfd --- /dev/null +++ b/e2e/flows/01-app-launch.yaml @@ -0,0 +1,7 @@ +appId: ${APP_ID} +--- +- launchApp +- assertVisible: "React Native.*" +- assertVisible: "Check for updates" +- assertVisible: "Clear updates" +- assertVisible: "Restart app" \ No newline at end of file diff --git a/e2e/flows/02-restart-no-crash.yaml b/e2e/flows/02-restart-no-crash.yaml new file mode 100644 index 000000000..4d7607686 --- /dev/null +++ b/e2e/flows/02-restart-no-crash.yaml @@ -0,0 +1,9 @@ +appId: ${APP_ID} +--- +- launchApp +- assertVisible: "React Native.*" +- tapOn: "Restart app" +- waitForAnimationToEnd: + timeout: 5000 +- assertVisible: "React Native.*" +- assertVisible: "Check for updates" \ No newline at end of file diff --git a/e2e/flows/03-update-flow.yaml b/e2e/flows/03-update-flow.yaml new file mode 100644 index 000000000..c940ec4c2 --- /dev/null +++ b/e2e/flows/03-update-flow.yaml @@ -0,0 +1,37 @@ +appId: ${APP_ID} +--- +- launchApp +- assertVisible: "React Native.*" + +# Ensure clean state — clear any previous updates +- tapOn: "Clear updates" +- tapOn: "Restart app" +- waitForAnimationToEnd: + timeout: 5000 +- assertVisible: "React Native.*" +- assertNotVisible: "UPDATED!" + +# Verify no running update metadata +- assertVisible: "METADATA_IDLE" +- tapOn: "Get update metadata" +- waitForAnimationToEnd: + timeout: 3000 +- assertVisible: "METADATA_NULL" + +# Check for updates — mandatory update installs silently +- tapOn: "Check for updates" + +# Wait for download + install + auto-restart (mandatory = IMMEDIATE) +- waitForAnimationToEnd: + timeout: 30000 + +# After restart, UPDATED! should be visible +- assertVisible: "UPDATED!" +- assertVisible: "React Native.*" + +# Verify UI is not frozen — metadata fetch should update the indicator +- assertVisible: "METADATA_IDLE" +- tapOn: "Get update metadata" +- waitForAnimationToEnd: + timeout: 3000 +- assertVisible: "METADATA_V1.0.1" diff --git a/e2e/helpers/build-app.ts b/e2e/helpers/build-app.ts new file mode 100644 index 000000000..a077294cb --- /dev/null +++ b/e2e/helpers/build-app.ts @@ -0,0 +1,71 @@ +import { execSync, spawn } from "child_process"; + +export async function buildApp( + appPath: string, + platform: "ios" | "android", + simulator?: string, +): Promise { + if (platform === "ios") { + await buildIos(appPath, simulator); + } else { + await buildAndroid(appPath); + } +} + +const DEFAULT_SIMULATOR = "iPhone 16"; + +function getBootedSimulatorName(): string | undefined { + try { + const output = execSync( + "xcrun simctl list devices booted -j", + { encoding: "utf8" }, + ); + const data = JSON.parse(output) as { + devices: Record>; + }; + for (const runtime of Object.values(data.devices)) { + const booted = runtime.find((d) => d.state === "Booted"); + if (booted) return booted.name; + } + } catch { + // ignore + } + return undefined; +} + +async function buildIos(appPath: string, simulator?: string): Promise { + console.log(`[command] npm run setup:pods (cwd: ${appPath})`); + await executeCommand("npm", ["run", "setup:pods"], appPath); + + const sim = simulator ?? getBootedSimulatorName() ?? DEFAULT_SIMULATOR; + const args = [ + "react-native", "run-ios", + "--mode", "Release", + "--no-packager", + "--simulator", sim, + ]; + console.log(`[command] npx ${args.join(" ")} (cwd: ${appPath})`); + return executeCommand("npx", args, appPath); +} + +function buildAndroid(appPath: string): Promise { + const args = [ + "react-native", "run-android", + "--mode", "release", + "--active-arch-only", + "--no-packager", + ]; + console.log(`[command] npx ${args.join(" ")} (cwd: ${appPath})`); + return executeCommand("npx", args, appPath); +} + +function executeCommand(command: string, args: string[], cwd: string): Promise { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { cwd, stdio: "inherit" }); + child.on("error", reject); + child.on("close", (code) => { + if (code === 0) resolve(); + else reject(new Error(`${command} ${args[0]} failed (exit code: ${code})`)); + }); + }); +} diff --git a/e2e/helpers/prepare-bundle.ts b/e2e/helpers/prepare-bundle.ts new file mode 100644 index 000000000..af42ea7f8 --- /dev/null +++ b/e2e/helpers/prepare-bundle.ts @@ -0,0 +1,105 @@ +import fs from "fs"; +import path from "path"; +import { spawn } from "child_process"; +import { MOCK_DATA_DIR, getMockServerHost } from "../config"; + +export function setReleasingBundle(appPath: string, value: boolean): void { + const appTsxPath = path.join(appPath, "App.tsx"); + let content = fs.readFileSync(appTsxPath, "utf8"); + content = content.replace( + value + ? /const IS_RELEASING_BUNDLE = false/ + : /const IS_RELEASING_BUNDLE = true/, + `const IS_RELEASING_BUNDLE = ${value}`, + ); + fs.writeFileSync(appTsxPath, content, "utf8"); +} + +const RELEASE_MARKER_PATTERN = /^console\.log\("E2E_MARKER_.*"\);$/m; + +/** + * Add a unique code statement to App.tsx to ensure different bundle hashes + * for releases with otherwise identical content. + */ +export function setReleaseMarker(appPath: string, version: string): void { + const appTsxPath = path.join(appPath, "App.tsx"); + let content = fs.readFileSync(appTsxPath, "utf8"); + const marker = `console.log("E2E_MARKER_${version}");`; + if (RELEASE_MARKER_PATTERN.test(content)) { + content = content.replace(RELEASE_MARKER_PATTERN, marker); + } else { + content = `${marker}\n${content}`; + } + fs.writeFileSync(appTsxPath, content, "utf8"); +} + +export function clearReleaseMarker(appPath: string): void { + const appTsxPath = path.join(appPath, "App.tsx"); + let content = fs.readFileSync(appTsxPath, "utf8"); + content = content.replace(RELEASE_MARKER_PATTERN, "").replace(/^\n+/, ""); + fs.writeFileSync(appTsxPath, content, "utf8"); +} + +export async function prepareBundle( + appPath: string, + platform: "ios" | "android", + appName: string, +): Promise { + setReleasingBundle(appPath, true); + + try { + await runCodePushCommand(appPath, platform, appName, [ + "code-push", "create-history", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", + "-p", platform, + "-i", appName, + ]); + await runCodePushRelease(appPath, platform, appName); + } finally { + setReleasingBundle(appPath, false); + } +} + +function runCodePushRelease( + appPath: string, + platform: "ios" | "android", + appName: string, +): Promise { + return runCodePushCommand(appPath, platform, appName, [ + "code-push", "release", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", + "-v", "1.0.1", + "-p", platform, + "-i", appName, + "-e", "index.js", + "-m", "true", + ]); +} + +export function runCodePushCommand( + appPath: string, + platform: "ios" | "android", + appName: string, + args: string[], +): Promise { + console.log(`[command] npx ${args.join(" ")} (cwd: ${appPath})`); + + return new Promise((resolve, reject) => { + const child = spawn("npx", args, { + cwd: appPath, + stdio: "inherit", + env: { + ...process.env, + E2E_MOCK_DATA_DIR: MOCK_DATA_DIR, + E2E_MOCK_SERVER_HOST: getMockServerHost(platform), + }, + }); + child.on("error", reject); + child.on("close", (code) => { + if (code === 0) resolve(); + else reject(new Error(`npx ${args[0]} ${args[1]} failed (exit code: ${code})`)); + }); + }); +} \ No newline at end of file diff --git a/e2e/helpers/prepare-config.ts b/e2e/helpers/prepare-config.ts new file mode 100644 index 000000000..b4aa4ce56 --- /dev/null +++ b/e2e/helpers/prepare-config.ts @@ -0,0 +1,56 @@ +import fs from "fs"; +import path from "path"; +import { getMockServerHost } from "../config"; + +const BACKUP_SUFFIX = ".e2e-backup"; + +export function prepareConfig(appPath: string, platform: "ios" | "android"): void { + patchAppTsx(appPath, platform); + copyLocalConfig(appPath); +} + +export function restoreConfig(appPath: string): void { + restoreFile(path.join(appPath, "App.tsx")); + const localConfig = path.join(appPath, "code-push.config.local.ts"); + if (fs.existsSync(localConfig)) { + fs.unlinkSync(localConfig); + } +} + +function patchAppTsx(appPath: string, platform: "ios" | "android"): void { + const appTsxPath = path.join(appPath, "App.tsx"); + backupFile(appTsxPath); + + let content = fs.readFileSync(appTsxPath, "utf8"); + const host = getMockServerHost(platform); + content = content.replace( + /const CODEPUSH_HOST = '[^']*'/, + `const CODEPUSH_HOST = '${host}'`, + ); + content = content.replace( + /const IS_RELEASING_BUNDLE = true/, + "const IS_RELEASING_BUNDLE = false", + ); + fs.writeFileSync(appTsxPath, content, "utf8"); + console.log(`App.tsx patched: CODEPUSH_HOST, IS_RELEASING_BUNDLE`); +} + +function copyLocalConfig(appPath: string): void { + const templatePath = path.resolve(__dirname, "../templates/code-push.config.local.ts"); + const destPath = path.join(appPath, "code-push.config.local.ts"); + fs.copyFileSync(templatePath, destPath); + console.log("code-push.config.local.ts copied to app directory"); +} + +function backupFile(filePath: string): void { + const backupPath = filePath + BACKUP_SUFFIX; + fs.copyFileSync(filePath, backupPath); +} + +function restoreFile(filePath: string): void { + const backupPath = filePath + BACKUP_SUFFIX; + if (fs.existsSync(backupPath)) { + fs.copyFileSync(backupPath, filePath); + fs.unlinkSync(backupPath); + } +} diff --git a/e2e/mock-server/server.ts b/e2e/mock-server/server.ts new file mode 100644 index 000000000..f137e7759 --- /dev/null +++ b/e2e/mock-server/server.ts @@ -0,0 +1,45 @@ +import express from "express"; +import { MOCK_DATA_DIR, MOCK_SERVER_PORT } from "../config"; +import type { Server } from "http"; + +let server: Server | null = null; + +export function startMockServer(): Promise { + return new Promise((resolve, reject) => { + const app = express(); + + app.use((req: express.Request, _res: express.Response, next: express.NextFunction) => { + console.log(`[mock-server] ${req.method} ${req.url}`); + next(); + }); + + app.use(express.static(MOCK_DATA_DIR)); + + app.use((_req: express.Request, res: express.Response) => { + res.status(404).json({ error: "Not found" }); + }); + + const s = app.listen(MOCK_SERVER_PORT, () => { + console.log(`Mock server started on port ${MOCK_SERVER_PORT}`); + console.log(`Serving files from: ${MOCK_DATA_DIR}`); + resolve(s); + }); + + s.on("error", reject); + server = s; + }); +} + +export function stopMockServer(): Promise { + return new Promise((resolve) => { + if (server) { + server.close(() => { + console.log("Mock server stopped"); + server = null; + resolve(); + }); + } else { + resolve(); + } + }); +} \ No newline at end of file diff --git a/e2e/run.ts b/e2e/run.ts new file mode 100644 index 000000000..4bd69704b --- /dev/null +++ b/e2e/run.ts @@ -0,0 +1,189 @@ +import { Command } from "commander"; +import { spawn } from "child_process"; +import path from "path"; +import fs from "fs"; +import { getAppPath, MOCK_DATA_DIR } from "./config"; +import { prepareConfig, restoreConfig } from "./helpers/prepare-config"; +import { prepareBundle, runCodePushCommand, setReleasingBundle, setReleaseMarker, clearReleaseMarker } from "./helpers/prepare-bundle"; +import { buildApp } from "./helpers/build-app"; +import { startMockServer, stopMockServer } from "./mock-server/server"; + +interface CliOptions { + app: string; + platform: "ios" | "android"; + simulator?: string; + maestroOnly?: boolean; +} + +const program = new Command() + .name("e2e") + .description("Run E2E tests with Maestro for CodePush example apps") + .requiredOption("--app ", "Example app name (e.g. RN0840RC5)") + .requiredOption("--platform ", "Platform: ios or android") + .option("--simulator ", "iOS simulator name (default: booted)") + .option("--maestro-only", "Skip build, only run Maestro flows", false); + +async function main() { + const options = program.parse(process.argv).opts(); + const appPath = getAppPath(options.app); + + if (!fs.existsSync(appPath)) { + console.error(`Example app not found: ${appPath}`); + process.exitCode = 1; + return; + } + + try { + // 1. Prepare config + console.log("\n=== [prepare] ==="); + prepareConfig(appPath, options.platform); + + // 2. Build (unless --maestro-only) + if (!options.maestroOnly) { + console.log("\n=== [build] ==="); + await buildApp(appPath, options.platform, options.simulator); + } + + // 3. Prepare update bundle + console.log("\n=== [prepare-bundle] ==="); + cleanMockData(); + await prepareBundle(appPath, options.platform, options.app); + + // 4. Start mock server + console.log("\n=== [start-mock-server] ==="); + await startMockServer(); + + // 5. Run Maestro — Phase 1: main flows + console.log("\n=== [run-maestro: phase 1] ==="); + const appId = getAppId(appPath, options.platform); + const flowsDir = path.resolve(__dirname, "flows"); + await runMaestro(flowsDir, options.platform, appId); + + // 6. Disable release for rollback test + console.log("\n=== [disable-release] ==="); + await runCodePushCommand(appPath, options.platform, options.app, [ + "code-push", "update-history", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", + "-v", "1.0.1", + "-p", options.platform, + "-i", options.app, + "-e", "false", + ]); + + // 7. Run Maestro — Phase 2: rollback to binary + console.log("\n=== [run-maestro: phase 2 (rollback to binary)] ==="); + const rollbackDir = path.resolve(__dirname, "flows-rollback"); + await runMaestro(rollbackDir, options.platform, appId); + + // 8. Prepare partial rollback: release 1.0.1 + 1.0.2 with different hashes + console.log("\n=== [prepare-bundle: partial rollback] ==="); + cleanMockData(); + setReleasingBundle(appPath, true); + try { + await runCodePushCommand(appPath, options.platform, options.app, [ + "code-push", "create-history", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", + "-p", options.platform, + "-i", options.app, + ]); + setReleaseMarker(appPath, "1.0.1"); + await runCodePushCommand(appPath, options.platform, options.app, [ + "code-push", "release", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", "-v", "1.0.1", + "-p", options.platform, "-i", options.app, + "-e", "index.js", "-m", "true", + ]); + setReleaseMarker(appPath, "1.0.2"); + await runCodePushCommand(appPath, options.platform, options.app, [ + "code-push", "release", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", "-v", "1.0.2", + "-p", options.platform, "-i", options.app, + "-e", "index.js", "-m", "true", + ]); + } finally { + clearReleaseMarker(appPath); + setReleasingBundle(appPath, false); + } + + // 9. Run Maestro — update to 1.0.2 + console.log("\n=== [run-maestro: partial rollback — update to 1.0.2] ==="); + const updateFlow = path.resolve(__dirname, "flows-partial-rollback/01-update-to-latest.yaml"); + await runMaestro(updateFlow, options.platform, appId); + + // 10. Disable only 1.0.2 → rollback target is 1.0.1 (not binary) + console.log("\n=== [disable-release: 1.0.2 only] ==="); + await runCodePushCommand(appPath, options.platform, options.app, [ + "code-push", "update-history", + "-c", "code-push.config.local.ts", + "-b", "1.0.0", "-v", "1.0.2", + "-p", options.platform, "-i", options.app, + "-e", "false", + ]); + + // 11. Run Maestro — rollback from 1.0.2 to 1.0.1 + console.log("\n=== [run-maestro: partial rollback — rollback to 1.0.1] ==="); + const rollbackFlow = path.resolve(__dirname, "flows-partial-rollback/02-rollback-to-previous.yaml"); + await runMaestro(rollbackFlow, options.platform, appId); + + console.log("\n=== E2E tests passed ==="); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(`\nE2E test failed: ${message}`); + process.exitCode = 1; + } finally { + // 8. Cleanup + console.log("\n=== [cleanup] ==="); + await stopMockServer(); + restoreConfig(appPath); + } +} + +function cleanMockData(): void { + if (fs.existsSync(MOCK_DATA_DIR)) { + fs.rmSync(MOCK_DATA_DIR, { recursive: true }); + } + fs.mkdirSync(MOCK_DATA_DIR, { recursive: true }); +} + +function getAppId(appPath: string, platform: "ios" | "android"): string { + if (platform === "ios") { + const appJsonPath = path.join(appPath, "app.json"); + const appJson = JSON.parse(fs.readFileSync(appJsonPath, "utf8")); + return `org.reactjs.native.example.${appJson.name}`; + } + // Android: read from build.gradle + const buildGradlePath = path.join(appPath, "android", "app", "build.gradle"); + const content = fs.readFileSync(buildGradlePath, "utf8"); + const match = content.match(/applicationId\s+"([^"]+)"/); + if (!match) { + throw new Error("Could not find applicationId in build.gradle"); + } + return match[1]; +} + +function runMaestro(flowsDir: string, platform: "ios" | "android", appId: string): Promise { + const args = ["test", flowsDir, "--env", `APP_ID=${appId}`]; + + if (platform === "android") { + args.push("--platform", "android"); + } else { + args.push("--platform", "ios"); + } + + console.log(`[command] maestro ${args.join(" ")}`); + + return new Promise((resolve, reject) => { + const child = spawn("maestro", args, { stdio: "inherit" }); + child.on("error", reject); + child.on("close", (code) => { + if (code === 0) resolve(); + else reject(new Error(`Maestro tests failed (exit code: ${code})`)); + }); + }); +} + +void main(); \ No newline at end of file diff --git a/e2e/templates/code-push.config.local.ts b/e2e/templates/code-push.config.local.ts new file mode 100644 index 000000000..1f2fc78af --- /dev/null +++ b/e2e/templates/code-push.config.local.ts @@ -0,0 +1,71 @@ +// @ts-nocheck +import { + CliConfigInterface, + ReleaseHistoryInterface, +} from "@bravemobile/react-native-code-push"; +import * as fs from "fs"; +import * as path from "path"; + +const MOCK_DATA_DIR = process.env.E2E_MOCK_DATA_DIR; +if (!MOCK_DATA_DIR) { + throw new Error("E2E_MOCK_DATA_DIR environment variable is required"); +} +const MOCK_SERVER_HOST = process.env.E2E_MOCK_SERVER_HOST; +if (!MOCK_SERVER_HOST) { + throw new Error("E2E_MOCK_SERVER_HOST environment variable is required"); +} + +function ensureDir(dir: string) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +const Config: CliConfigInterface = { + bundleUploader: async ( + source: string, + platform: "ios" | "android", + identifier = "staging", + ): Promise<{ downloadUrl: string }> => { + const fileName = path.basename(source); + const destDir = path.join(MOCK_DATA_DIR, "bundles", platform, identifier); + ensureDir(destDir); + const destPath = path.join(destDir, fileName); + fs.copyFileSync(source, destPath); + + const downloadUrl = `${MOCK_SERVER_HOST}/bundles/${platform}/${identifier}/${fileName}`; + console.log("Bundle copied to:", destPath); + console.log("Download URL:", downloadUrl); + return { downloadUrl }; + }, + + getReleaseHistory: async ( + targetBinaryVersion: string, + platform: "ios" | "android", + identifier = "staging", + ): Promise => { + const jsonPath = path.join( + MOCK_DATA_DIR, "histories", platform, identifier, `${targetBinaryVersion}.json`, + ); + if (!fs.existsSync(jsonPath)) { + return {} as ReleaseHistoryInterface; + } + return JSON.parse(fs.readFileSync(jsonPath, "utf8")); + }, + + setReleaseHistory: async ( + targetBinaryVersion: string, + jsonFilePath: string, + _releaseInfo: ReleaseHistoryInterface, + platform: "ios" | "android", + identifier = "staging", + ): Promise => { + const destDir = path.join(MOCK_DATA_DIR, "histories", platform, identifier); + ensureDir(destDir); + const destPath = path.join(destDir, `${targetBinaryVersion}.json`); + fs.copyFileSync(jsonFilePath, destPath); + console.log("Release history saved to:", destPath); + }, +}; + +module.exports = Config; \ No newline at end of file diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 000000000..cc40c40f3 --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "moduleResolution": "Node16", + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["./**/*.ts"] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fc8137330..3b7078b17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,29 +28,18 @@ "@babel/preset-env": "^7.26.0", "@babel/preset-typescript": "^7.27.1", "@eslint/js": "^9.13.0", - "@types/assert": "^1.5.2", - "@types/mkdirp": "^1.0.1", - "@types/mocha": "^9.0.0", + "@types/express": "^5.0.6", "@types/node": "^18.19.129", - "@types/q": "^1.5.4", "@types/semver": "^7.5.8", "@types/shelljs": "^0.8.15", "@types/yauzl": "^2.10.3", - "archiver": "latest", "babel-jest": "^29.7.0", - "body-parser": "latest", - "code-push-plugin-testing-framework": "file:./code-push-plugin-testing-framework", "eslint": "^9.13.0", "eslint-plugin-react": "^7.37.2", "express": "latest", "globals": "^15.11.0", "jest": "^29.7.0", - "mkdirp": "latest", - "mocha": "^9.2.0", - "q": "^1.5.1", - "slash": "^3.0.0", "ts-node": "^10.9.2", - "tslint": "^6.1.3", "typescript": "5.0.4", "typescript-eslint": "^8.11.0" }, @@ -95,7 +84,7 @@ }, "code-push-plugin-testing-framework": { "version": "0.0.1", - "dev": true, + "extraneous": true, "license": "MIT", "dependencies": { "@types/uuid": "^8.3.1", @@ -109,306 +98,6 @@ "uuid": "^8.3.2" } }, - "code-push-plugin-testing-framework/node_modules/@types/uuid": { - "version": "8.3.1", - "dev": true, - "license": "MIT" - }, - "code-push-plugin-testing-framework/node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "code-push-plugin-testing-framework/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "code-push-plugin-testing-framework/node_modules/base-64": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "code-push-plugin-testing-framework/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "code-push-plugin-testing-framework/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "code-push-plugin-testing-framework/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "code-push-plugin-testing-framework/node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "code-push-plugin-testing-framework/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "code-push-plugin-testing-framework/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "code-push-plugin-testing-framework/node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "code-push-plugin-testing-framework/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "code-push-plugin-testing-framework/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "code-push-plugin-testing-framework/node_modules/mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", - "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "code-push-plugin-testing-framework/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "code-push-plugin-testing-framework/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "code-push-plugin-testing-framework/node_modules/superagent": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", - "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", - "readable-stream": "^3.6.0", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 7.0.0" - } - }, - "code-push-plugin-testing-framework/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "code-push-plugin-testing-framework/node_modules/uuid": { - "version": "8.3.2", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "code-push-plugin-testing-framework/node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true, - "license": "Apache-2.0" - }, - "code-push-plugin-testing-framework/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "code-push-plugin-testing-framework/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "code-push-plugin-testing-framework/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "license": "Apache-2.0", @@ -2607,160 +2296,57 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", "dev": true, "license": "ISC", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "p-locate": "^4.1.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", "dev": true, "license": "MIT", "engines": { @@ -3117,17 +2703,6 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@react-native-community/cli": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-10.0.0.tgz", @@ -4456,14 +4031,6 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "devOptional": true, @@ -4484,11 +4051,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@types/assert": { - "version": "1.5.2", - "dev": true, - "license": "MIT" - }, "node_modules/@types/babel__core": { "version": "7.20.5", "dev": true, @@ -4526,11 +4088,57 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "dev": true, "license": "MIT" }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/@types/glob": { "version": "7.2.0", "dev": true, @@ -4548,6 +4156,13 @@ "@types/node": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "license": "MIT" @@ -4576,19 +4191,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mkdirp": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mocha": { - "version": "9.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "18.19.129", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.129.tgz", @@ -4598,8 +4200,17 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/q": { - "version": "1.5.4", + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true, "license": "MIT" }, @@ -4608,6 +4219,27 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, "node_modules/@types/shelljs": { "version": "0.8.15", "dev": true, @@ -4922,11 +4554,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "dev": true, - "license": "ISC" - }, "node_modules/@xmldom/xmldom": { "version": "0.8.11", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", @@ -4941,6 +4568,7 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", + "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -4991,17 +4619,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "dev": true, @@ -5023,14 +4640,6 @@ "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", "peer": true }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.1", "dev": true, @@ -5114,125 +4723,6 @@ "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", "peer": true }, - "node_modules/archiver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", - "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.2", - "async": "^3.2.4", - "buffer-crc32": "^1.0.0", - "readable-stream": "^4.0.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^3.0.0", - "zip-stream": "^6.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", - "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^10.0.0", - "graceful-fs": "^4.2.0", - "is-stream": "^2.0.1", - "lazystream": "^1.0.0", - "lodash": "^4.17.15", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/archiver/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/arg": { "version": "4.1.3", "devOptional": true, @@ -5378,22 +4868,6 @@ "license": "MIT", "peer": true }, - "node_modules/ast-types": { - "version": "0.13.4", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ast-types/node_modules/tslib": { - "version": "2.2.0", - "dev": true, - "license": "0BSD" - }, "node_modules/astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -5405,7 +4879,8 @@ }, "node_modules/async": { "version": "3.2.4", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/async-limiter": { "version": "1.0.1", @@ -5413,11 +4888,6 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "peer": true }, - "node_modules/asynckit": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "dev": true, @@ -5432,13 +4902,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -5648,14 +5111,6 @@ "version": "1.0.0", "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", - "dev": true, - "license": "Apache-2.0", - "optional": true - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5685,14 +5140,6 @@ "node": ">=0.6" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -5805,11 +5252,6 @@ "node": ">=8" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "dev": true, - "license": "ISC" - }, "node_modules/browserslist": { "version": "4.24.2", "funding": [ @@ -5847,31 +5289,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/buffer-crc32": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", @@ -5885,14 +5302,6 @@ "version": "1.1.2", "license": "MIT" }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -6006,40 +5415,6 @@ "node": ">=10" } }, - "node_modules/charenc": { - "version": "0.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/ci-info": { "version": "3.9.0", "funding": [ @@ -6082,16 +5457,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cliui": { - "version": "7.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -6124,10 +5489,6 @@ "node": ">= 0.12.0" } }, - "node_modules/code-push-plugin-testing-framework": { - "resolved": "code-push-plugin-testing-framework", - "link": true - }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "dev": true, @@ -6153,17 +5514,6 @@ "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "peer": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", @@ -6185,45 +5535,6 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "peer": true }, - "node_modules/component-emitter": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/compress-commons": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/compress-commons/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -6398,11 +5709,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookiejar": { - "version": "2.1.4", - "dev": true, - "license": "MIT" - }, "node_modules/core-js-compat": { "version": "3.38.1", "license": "MIT", @@ -6416,7 +5722,8 @@ }, "node_modules/core-util-is": { "version": "1.0.2", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/cosmiconfig": { "version": "5.2.1", @@ -6468,50 +5775,6 @@ "node": ">=4" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", - "dev": true, - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/crc32-stream/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/create-jest": { "version": "29.7.0", "dev": true, @@ -6549,22 +5812,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/data-view-buffer": { "version": "1.0.1", "dev": true, @@ -6637,6 +5884,7 @@ "node_modules/decamelize": { "version": "1.2.0", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -6711,14 +5959,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/denodeify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", @@ -6759,24 +5999,6 @@ "node": ">=8" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-sequences": { "version": "29.6.3", "dev": true, @@ -6796,13 +6018,6 @@ "node": ">=0.10.0" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -7048,35 +6263,6 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "1.14.3", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, "node_modules/eslint": { "version": "9.13.0", "dev": true, @@ -7389,14 +6575,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "license": "BSD-2-Clause", @@ -7418,20 +6596,11 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "license": "MIT", @@ -7540,13 +6709,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-glob": { "version": "3.3.2", "license": "MIT", @@ -7571,11 +6733,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "dev": true, - "license": "MIT" - }, "node_modules/fast-xml-parser": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", @@ -7619,14 +6776,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/fill-range": { "version": "7.1.1", "license": "MIT", @@ -7781,14 +6930,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "4.0.1", "dev": true, @@ -7823,77 +6964,27 @@ "is-callable": "^1.1.3" } }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "node_modules/forwarded": { + "version": "0.2.0", "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.6" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.6" } }, - "node_modules/form-data": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", + "node_modules/fs-extra": { + "version": "8.1.0", "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -7918,33 +7009,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/ftp": { - "version": "0.3.10", - "dev": true, - "dependencies": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ftp/node_modules/readable-stream": { - "version": "1.1.14", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/ftp/node_modules/string_decoder": { - "version": "0.10.31", - "dev": true, - "license": "MIT" - }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -8043,22 +7107,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-uri": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "data-uri-to-buffer": "3", - "debug": "4", - "file-uri-to-path": "2", - "fs-extra": "^8.1.0", - "ftp": "^0.3.10" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/glob": { "version": "7.2.0", "license": "ISC", @@ -8133,14 +7181,6 @@ "dev": true, "license": "MIT" }, - "node_modules/growl": { - "version": "1.10.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.x" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "dev": true, @@ -8213,14 +7253,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, "node_modules/hermes-estree": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.8.0.tgz", @@ -8257,15 +7289,6 @@ "node": ">= 8" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "license": "BSD-3-Clause", @@ -8292,31 +7315,6 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/human-signals": { "version": "2.1.0", "license": "Apache-2.0", @@ -8353,7 +7351,8 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/ignore": { "version": "5.3.2", @@ -8449,28 +7448,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/ip": { - "version": "1.1.9", - "dev": true, - "license": "MIT" - }, - "node_modules/ip-address": { - "version": "9.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "dev": true, @@ -8523,17 +7500,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.1.2", "dev": true, @@ -8719,14 +7685,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -8834,6 +7792,7 @@ "node_modules/is-unicode-supported": { "version": "0.1.0", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -8887,11 +7846,6 @@ "node": ">=4" } }, - "node_modules/isarray": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "license": "ISC" @@ -8981,22 +7935,6 @@ "node": ">= 0.4" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jest": { "version": "29.7.0", "dev": true, @@ -9649,11 +8587,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/jsc-android": { "version": "250230.2.1", "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-250230.2.1.tgz", @@ -9784,6 +8717,7 @@ "node_modules/jsonfile": { "version": "4.0.0", "license": "MIT", + "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -9826,59 +8760,6 @@ "node": ">=6" } }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/leven": { "version": "3.1.0", "license": "MIT", @@ -9886,18 +8767,6 @@ "node": ">=6" } }, - "node_modules/levn": { - "version": "0.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "dev": true, @@ -9916,13 +8785,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "license": "MIT" @@ -9941,6 +8803,7 @@ "node_modules/log-symbols": { "version": "4.1.0", "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -10106,21 +8969,6 @@ "tmpl": "1.0.5" } }, - "node_modules/md5": { - "version": "2.3.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/md5/node_modules/is-buffer": { - "version": "1.1.6", - "dev": true, - "license": "MIT" - }, "node_modules/media-typer": { "version": "0.3.0", "dev": true, @@ -11132,6 +9980,7 @@ "node_modules/mime": { "version": "2.6.0", "license": "MIT", + "peer": true, "bin": { "mime": "cli.js" }, @@ -11176,202 +10025,25 @@ "node_modules/minimist": { "version": "1.2.7", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "node_modules/natural-compare": { + "version": "1.4.0", "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "MIT" }, - "node_modules/mocha": { - "version": "9.2.2", - "dev": true, + "node_modules/negotiator": { + "version": "0.6.3", "license": "MIT", - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha-junit-reporter": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", - "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "md5": "^2.3.0", - "mkdirp": "^3.0.0", - "strip-ansi": "^6.0.1", - "xml": "^1.0.1" - }, - "peerDependencies": { - "mocha": ">=2.2.5" - } - }, - "node_modules/mocha-junit-reporter/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha-junit-reporter/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">= 0.6" } }, "node_modules/neo-async": { @@ -11380,14 +10052,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "peer": true }, - "node_modules/netmask": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -11625,22 +10289,6 @@ "node": ">=8" } }, - "node_modules/optionator": { - "version": "0.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -11728,13 +10376,6 @@ "node": ">=6" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/parent-module": { "version": "1.0.1", "dev": true, @@ -11797,30 +10438,6 @@ "version": "1.0.7", "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -11931,13 +10548,6 @@ "node": ">= 0.4" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/pretty-format": { "version": "29.7.0", "license": "MIT", @@ -11964,21 +10574,12 @@ "version": "18.3.1", "license": "MIT" }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/promise": { "version": "8.3.0", @@ -12021,11 +10622,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -12059,15 +10655,6 @@ ], "license": "MIT" }, - "node_modules/q": { - "version": "1.5.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -12102,14 +10689,6 @@ ], "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -12338,6 +10917,7 @@ "node_modules/readable-stream": { "version": "3.6.0", "license": "MIT", + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12347,50 +10927,6 @@ "node": ">= 6" } }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", @@ -12521,256 +11057,62 @@ "regjsparser": "bin/parser" } }, - "node_modules/replace": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/replace/-/replace-1.2.2.tgz", - "integrity": "sha512-C4EDifm22XZM2b2JOYe6Mhn+lBsLBAvLbK8drfUQLTfD1KYl/n3VaW/CDju0Ny4w3xTtegBpg8YNSpFJPUDSjA==", - "dev": true, + "node_modules/require-directory": { + "version": "2.1.1", "license": "MIT", - "dependencies": { - "chalk": "2.4.2", - "minimatch": "3.0.5", - "yargs": "^15.3.1" - }, - "bin": { - "replace": "bin/replace.js", - "search": "bin/search.js" - }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/replace/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, + "node_modules/require-main-filename": { + "version": "2.0.0", + "license": "ISC", + "peer": true + }, + "node_modules/resolve": { + "version": "1.22.8", "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=4" + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/replace/node_modules/chalk": { - "version": "2.4.2", + "node_modules/resolve-cwd": { + "version": "3.0.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/replace/node_modules/cliui": { - "version": "6.0.0", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/replace/node_modules/color-convert": { - "version": "1.9.3", + "node_modules/resolve-from": { + "version": "4.0.0", "dev": true, "license": "MIT", - "dependencies": { - "color-name": "1.1.3" + "engines": { + "node": ">=4" } }, - "node_modules/replace/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/replace/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/replace/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/replace/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/replace/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/replace/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/replace/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/replace/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/replace/node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/replace/node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/replace/node_modules/yargs": { - "version": "15.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/replace/node_modules/yargs-parser": { - "version": "18.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/resolve": { - "version": "1.22.8", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", + "node_modules/resolve.exports": { + "version": "2.0.2", "dev": true, "license": "MIT", "engines": { @@ -12988,14 +11330,6 @@ "node": ">=0.10.0" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -13013,7 +11347,8 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/set-function-length": { "version": "1.2.2", @@ -13196,41 +11531,6 @@ "node": ">=4" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "4", - "socks": "^2.3.3" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/source-map": { "version": "0.6.1", "license": "BSD-3-Clause", @@ -13311,23 +11611,10 @@ "node": ">= 0.10.0" } }, - "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -13356,22 +11643,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "dev": true, @@ -13462,20 +11733,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "4.0.0", "dev": true, @@ -13504,178 +11761,32 @@ "version": "3.1.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.1.tgz", - "integrity": "sha512-O7aCHfYCamLCctjAiaucmE+fHf2DYHkus2OKCn4Wv03sykfFtgeECn505X6K4mPl8CRNd/qurC9guq+ynoN4pw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "peer": true - }, - "node_modules/sudo-prompt": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "peer": true - }, - "node_modules/superagent": { - "version": "8.1.2", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent-proxy": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.2", - "proxy-agent": "^5.0.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "superagent": ">= 0.15.4 || 1 || 2 || 3" - } - }, - "node_modules/superagent-proxy/node_modules/degenerator": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.2", - "escodegen": "^1.8.1", - "esprima": "^4.0.0", - "vm2": "^3.9.17" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/superagent-proxy/node_modules/pac-proxy-agent": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4", - "get-uri": "3", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "5", - "pac-resolver": "^5.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "5" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/superagent-proxy/node_modules/pac-resolver": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "degenerator": "^3.0.2", - "ip": "^1.1.5", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/superagent-proxy/node_modules/proxy-agent": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.0", - "debug": "4", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "lru-cache": "^5.1.1", - "pac-proxy-agent": "^5.0.0", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^5.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "4.3.4", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/superagent/node_modules/form-data": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/superagent/node_modules/formidable": { - "version": "2.1.2", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" + "engines": { + "node": ">=8" }, "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.1.tgz", + "integrity": "sha512-O7aCHfYCamLCctjAiaucmE+fHf2DYHkus2OKCn4Wv03sykfFtgeECn505X6K4mPl8CRNd/qurC9guq+ynoN4pw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "peer": true + }, + "node_modules/sudo-prompt": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "peer": true + }, "node_modules/supports-color": { "version": "7.2.0", "license": "MIT", @@ -13696,18 +11807,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", @@ -13778,16 +11877,6 @@ "node": ">=8" } }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, "node_modules/text-table": { "version": "0.2.0", "dev": true, @@ -13933,150 +12022,6 @@ "node": ">=0.3.1" } }, - "node_modules/tslib": { - "version": "1.13.0", - "dev": true, - "license": "0BSD" - }, - "node_modules/tslint": { - "version": "6.1.3", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/tslint/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/tslint/node_modules/commander": { - "version": "2.20.3", - "dev": true, - "license": "MIT" - }, - "node_modules/tslint/node_modules/diff": { - "version": "4.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslint/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/mkdirp": { - "version": "0.5.5", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tslint/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tsutils": { - "version": "2.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "node_modules/type-check": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "license": "MIT", @@ -14289,6 +12234,7 @@ "node_modules/universalify": { "version": "0.1.2", "license": "MIT", + "peer": true, "engines": { "node": ">= 4.0.0" } @@ -14347,7 +12293,8 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/utils-merge": { "version": "1.0.1", @@ -14396,21 +12343,6 @@ "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", "peer": true }, - "node_modules/vm2": { - "version": "3.9.19", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.7.0", - "acorn-walk": "^8.2.0" - }, - "bin": { - "vm2": "bin/vm2" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/walker": { "version": "1.0.8", "license": "Apache-2.0", @@ -14526,7 +12458,8 @@ }, "node_modules/which-module": { "version": "2.0.0", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/which-typed-array": { "version": "1.1.15", @@ -14554,11 +12487,6 @@ "node": ">=0.10.0" } }, - "node_modules/workerpool": { - "version": "6.2.0", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/wrap-ansi": { "version": "7.0.0", "license": "MIT", @@ -14574,25 +12502,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "license": "ISC" @@ -14631,11 +12540,6 @@ "node": ">=10.0.0" } }, - "node_modules/xml": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -14645,11 +12549,6 @@ "node": ">=8.0" } }, - "node_modules/xregexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -14661,81 +12560,13 @@ }, "node_modules/y18n": { "version": "4.0.3", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/yallist": { "version": "3.1.1", "license": "ISC" }, - "node_modules/yargs": { - "version": "16.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/yauzl": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", @@ -14784,38 +12615,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zip-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", - "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.0", - "compress-commons": "^6.0.2", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/zip-stream/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } } } } diff --git a/package.json b/package.json index c7fa17c93..30f653928 100644 --- a/package.json +++ b/package.json @@ -53,24 +53,15 @@ "scripts": { "setup": "npm install --quiet --no-progress", "setup-example-app": "ts-node --project scripts/setupExampleApp/tsconfig.json scripts/setupExampleApp/runSetupExampleApp.ts", - "test": "npm run build:tests && npm run test:setup && npm run test:fast", - "test:android": "npm run build:tests && npm run test:setup:android && npm run test:fast:android", - "test:ios": "npm run build:tests && npm run test:setup:ios && npm run test:fast:ios", - "test:setup": "mocha --recursive bin/test --android --ios --setup", - "test:setup:android": "mocha --recursive bin/test --android --setup", - "test:setup:ios": "mocha --recursive bin/test --ios --setup", - "test:fast": "mocha --recursive bin/test --android --ios", - "test:fast:android": "mocha --recursive bin/test --android", - "test:fast:ios": "mocha --recursive bin/test --ios", - "test:debugger:android": "mocha --recursive --inspect-brk=0.0.0.0 bin/test --android", - "test:debugger:ios": "mocha --recursive --inspect-brk=0.0.0.0 bin/test --ios", - "tslint": "tslint -c tslint.json test/**/*.ts", "type:cli": "npm run --workspace cli typecheck", + "type:e2e": "tsc --project e2e/tsconfig.json --noEmit", + "typecheck": "npm run type:cli && npm run type:e2e", "build:cli": "npm run --workspace cli clean && npm run --workspace cli build", "prepack": "npm run build:cli", "publish": "npm publish --access=public", "eslint": "eslint --quiet .", - "jest": "jest src/versioning/* expo/* && npm run --workspace cli test" + "jest": "jest src/versioning/* expo/* && npm run --workspace cli test", + "e2e": "ts-node --project e2e/tsconfig.json e2e/run.ts" }, "repository": { "type": "git", @@ -82,8 +73,8 @@ "semver": "^7.3.5", "shelljs": "^0.10.0", "xcode": "^3.0.1", - "yazl": "^3.3.1", - "yauzl": "^3.2.0" + "yauzl": "^3.2.0", + "yazl": "^3.3.1" }, "peerDependencies": { "expo": ">=50.0.0", @@ -99,29 +90,18 @@ "@babel/preset-env": "^7.26.0", "@babel/preset-typescript": "^7.27.1", "@eslint/js": "^9.13.0", - "@types/assert": "^1.5.2", - "@types/mkdirp": "^1.0.1", - "@types/mocha": "^9.0.0", + "@types/express": "^5.0.6", "@types/node": "^18.19.129", - "@types/q": "^1.5.4", "@types/semver": "^7.5.8", "@types/shelljs": "^0.8.15", "@types/yauzl": "^2.10.3", - "archiver": "latest", "babel-jest": "^29.7.0", - "body-parser": "latest", - "code-push-plugin-testing-framework": "file:./code-push-plugin-testing-framework", "eslint": "^9.13.0", "eslint-plugin-react": "^7.37.2", "express": "latest", "globals": "^15.11.0", "jest": "^29.7.0", - "mkdirp": "latest", - "mocha": "^9.2.0", - "q": "^1.5.1", - "slash": "^3.0.0", "ts-node": "^10.9.2", - "tslint": "^6.1.3", "typescript": "5.0.4", "typescript-eslint": "^8.11.0" }, diff --git a/scripts/setupExampleApp/runSetupExampleApp.ts b/scripts/setupExampleApp/runSetupExampleApp.ts index cefa026b0..ab73301d1 100644 --- a/scripts/setupExampleApp/runSetupExampleApp.ts +++ b/scripts/setupExampleApp/runSetupExampleApp.ts @@ -201,6 +201,18 @@ async function configureIosVersioning(context: SetupContext): Promise { "IPHONEOS_DEPLOYMENT_TARGET = 16.0;", "IPHONEOS_DEPLOYMENT_TARGET" ); + nextContent = replaceAllOrThrow( + nextContent, + /"CODE_SIGN_IDENTITY\[sdk=iphoneos\*\]" = "iPhone Developer";/g, + '"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";\n\t\t\t\tCODE_SIGNING_ALLOWED = NO;\n\t\t\t\tCODE_SIGNING_REQUIRED = NO;', + "CODE_SIGN_IDENTITY" + ); + nextContent = replaceAllOrThrow( + nextContent, + /SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";/g, + 'SUPPORTED_PLATFORMS = iphonesimulator;', + "SUPPORTED_PLATFORMS" + ); return nextContent; }); @@ -237,6 +249,23 @@ async function configureAndroidVersioning(context: SetupContext): Promise ); return next; }); + + const manifestPath = path.join( + context.projectPath, + "android", + "app", + "src", + "main", + "AndroidManifest.xml" + ); + updateTextFile(manifestPath, (content) => + replaceAllOrThrow( + content, + /android:usesCleartextTraffic="\$\{usesCleartextTraffic\}"/, + 'android:usesCleartextTraffic="true"', + "usesCleartextTraffic" + ) + ); } async function configureLocalCodeLink(context: SetupContext): Promise { diff --git a/scripts/setupExampleApp/templates/App.tsx.txt b/scripts/setupExampleApp/templates/App.tsx.txt index 2edb38548..d8e752227 100644 --- a/scripts/setupExampleApp/templates/App.tsx.txt +++ b/scripts/setupExampleApp/templates/App.tsx.txt @@ -1,6 +1,5 @@ import React, { useCallback, useState } from 'react'; import { - Alert, Button, Platform, ScrollView, @@ -34,7 +33,7 @@ function App() { const handleSync = useCallback(() => { CodePush.sync( - { updateDialog: true }, + {}, status => { setSyncResult(findKeyByValue(CodePush.SyncStatus, status) ?? ''); }, @@ -42,11 +41,11 @@ function App() { setProgress(Math.round((receivedBytes / totalBytes) * 100)); }, mismatch => { - Alert.alert('CodePush mismatch', JSON.stringify(mismatch, null, 2)); + console.log('CodePush mismatch', JSON.stringify(mismatch, null, 2)); }, ).catch(error => { console.error(error); - Alert.alert('Sync failed', error.message ?? 'Unknown error'); + console.log('Sync failed', error.message ?? 'Unknown error'); }); }, []); @@ -56,9 +55,9 @@ function App() { CodePush.getUpdateMetadata(CodePush.UpdateState.PENDING), CodePush.getUpdateMetadata(CodePush.UpdateState.LATEST), ]); - setRunningMetadata(JSON.stringify(running, null, 2)); - setPendingMetadata(JSON.stringify(pending, null, 2)); - setLatestMetadata(JSON.stringify(latest, null, 2)); + setRunningMetadata(JSON.stringify(running ?? null, null, 2)); + setPendingMetadata(JSON.stringify(pending ?? null, null, 2)); + setLatestMetadata(JSON.stringify(latest ?? null, null, 2)); }, []); return ( @@ -88,6 +87,7 @@ function App() { />