Make it possible to pass multiple supported extensions to configureApp helper
MODIFY
lib/basedriver/helpers.js
lib/basedriver/helpers.js
@@ -7,86 +7,111 @@ import B from 'bluebird';
import { tempDir, system, fs, util, zip } from 'appium-support';
import { exec } from 'teen_process';
import request from 'request';
+import asyncRequest from 'request-promise';
+import LRU from 'lru-cache';
const ZIP_EXTS = ['.zip', '.ipa'];
const ZIP_MIME_TYPE = 'application/zip';
-const DOWNLOADS_CACHE = {};
+const DOWNLOADS_CACHE = new LRU({
+ max: 100,
+});
-async function getModificationDate (url) {
- let response;
+async function retrieveHeaders (link) {
try {
- response = await request.head(url);
+ const response = await asyncRequest({
+ url: link,
+ method: 'HEAD',
+ resolveWithFullResponse: true,
+ timeout: 5000,
+ });
+ return response.headers;
} catch (e) {
- logger.debug(`Cannot send HEAD request to '${url}'. Original error: ${e.message}`);
- return null;
+ logger.debug(`Cannot send HEAD request to '${link}'. Original error: ${e.message}`);
}
- const value = response.headers['last-modified'];
- logger.debug(`Got '${value}' as 'Last-Modified' HEAD response header value of '${url}'`);
- return value ? new Date(value) : null;
+ return {};
}
-async function getCachedApplicationPath (url) {
- const appInfo = DOWNLOADS_CACHE[url];
- if (!appInfo) {
+function getCachedApplicationPath (link, currentModified) {
+ if (!DOWNLOADS_CACHE.has(link) || !currentModified) {
return null;
}
- const {lastModified, fullPath} = appInfo;
- const currentModified = await getModificationDate(url);
- if (!currentModified) {
- return null;
- }
- if (currentModified <= lastModified) {
- logger.debug(`'Last-Modified' response header value of '${url}' is less or equal in comparison to the ` +
- `cached value '${lastModified}'`);
+
+ const {lastModified, fullPath} = DOWNLOADS_CACHE.get(link);
+ if (currentModified.getTime() <= lastModified.getTime()) {
+ logger.debug(`Reusing already downloaded application at '${fullPath}'`);
return fullPath;
}
- logger.debug(`'Last-Modified' response header value of '${url}' is greater in comparison to the ` +
- `cached value '${lastModified}'`);
+ logger.debug(`'Last-Modified' timestamp of '${link}' has been updated. ` +
+ `The updated copy of the application is going to be downloaded.`);
return null;
}
-async function configureApp (app, appExt, mountRoot = "Volumes", windowsShareUserName = "", windowsSharePassword = "") {
+async function configureApp (app, supportedAppExtensions, mountRoot = "Volumes", windowsShareUserName = "", windowsSharePassword = "") {
if (!_.isString(app)) {
// immediately shortcircuit if not given an app
return;
}
+ if (!_.isArray(supportedAppExtensions)) {
+ supportedAppExtensions = [supportedAppExtensions];
+ }
let newApp = app;
let shouldUnzipApp = false;
const {protocol} = url.parse(newApp);
const isUrl = ['http:', 'https:'].includes(protocol);
+ let currentModified = null;
if (newApp.startsWith('\\\\')) {
// Use the app from Windows network share
logger.info(`Downloading the application '${newApp}' from SMB share...`);
- newApp = await copyFromWindowsNetworkShare(newApp, appExt,
- mountRoot, windowsShareUserName, windowsSharePassword);
+ newApp = await copyFromWindowsNetworkShare(newApp, mountRoot, windowsShareUserName, windowsSharePassword);
if (!await fs.exists(newApp)) {
throw new Error(`The application at '${app}' does not exist or is not accessible for download`);
}
logger.info(`Downloaded the app to '${newApp}'`);
} else if (isUrl) {
// Use the app from remote URL
- const cachedPath = await getCachedApplicationPath(newApp);
+ logger.info(`Using downloadable app '${newApp}'`);
+ const headers = await retrieveHeaders(newApp);
+ if (headers['last-modified']) {
+ logger.debug(`Last-Modified: ${headers['last-modified']}`);
+ currentModified = new Date(headers['last-modified']);
+ }
+ const cachedPath = getCachedApplicationPath(newApp, currentModified);
if (cachedPath) {
if (await fs.exists(cachedPath)) {
logger.info(`Reusing the previously downloaded application at '${cachedPath}'`);
return cachedPath;
}
logger.info(`The application at '${cachedPath}' does not exist anymore. Deleting it from the cache`);
- delete DOWNLOADS_CACHE[newApp];
+ DOWNLOADS_CACHE.del(newApp);
}
- logger.info(`Using downloadable app '${newApp}'`);
- const {targetPath, contentType} = await downloadApp(newApp, appExt);
- newApp = targetPath;
+ let fileName = `appium-app${_.first(supportedAppExtensions)}`;
+ if (headers['content-type']) {
+ logger.debug(`Content-Type: ${headers['content-type']}`);
// the filetype may not be obvious for certain urls, so check the mime type too
- shouldUnzipApp = _.includes(ZIP_EXTS, path.extname(newApp)) || contentType === ZIP_MIME_TYPE;
- logger.info(`Downloaded the app to '${newApp}'`);
+ if (headers['content-type'] === ZIP_MIME_TYPE) {
+ fileName = 'appium-app.zip';
+ shouldUnzipApp = true;
+ }
+ }
+ if (headers['content-disposition'] && /^attachment/i.test(headers['content-disposition'])) {
+ const match = /filename="([^"]+)/i.exec(headers['content-disposition']);
+ if (match) {
+ fileName = match[1];
+ logger.debug(`Parsed file name '${fileName}' from 'Content-Disposition' header`);
+ shouldUnzipApp = shouldUnzipApp || _.includes(ZIP_EXTS, path.extname(fileName));
+ }
+ }
+ const targetPath = await tempDir.path({
+ prefix: fileName,
+ suffix: '',
+ });
+ newApp = await downloadApp(newApp, targetPath);
} else if (await fs.exists(newApp)) {
// Use the local app
logger.info(`Using local app '${newApp}'`);
shouldUnzipApp = _.includes(ZIP_EXTS, path.extname(newApp));
- newApp = shouldUnzipApp ? await copyLocalZip(newApp) : newApp;
} else {
let errorMessage = `The application at '${newApp}' does not exist or is not accessible`;
// protocol value for 'C:\\temp' is 'c:', so we check the length as well
@@ -98,40 +123,36 @@ async function configureApp (app, appExt, mountRoot = "Volumes", windowsShareUse
}
if (shouldUnzipApp) {
- logger.info(`Unzipping local app '${newApp}'...`);
+ const tmpRoot = await tempDir.openDir();
const archivePath = newApp;
try {
- newApp = await unzipApp(archivePath, appExt);
+ newApp = await unzipApp(archivePath, tmpRoot, supportedAppExtensions);
} finally {
+ if (newApp !== archivePath && archivePath !== app) {
await fs.rimraf(archivePath);
}
+ }
logger.info(`Unzipped local app to '${newApp}'`);
}
- if (path.extname(newApp) !== appExt) {
+ if (!supportedAppExtensions.includes(path.extname(newApp))) {
if (newApp !== app) {
await fs.rimraf(newApp);
}
- throw new Error(`New app path '${newApp}' did not have extension '${appExt}'`);
+ throw new Error(`New app path '${newApp}' did not have extension(s) '${supportedAppExtensions}'`);
}
- if (isUrl) {
- const lastModified = await getModificationDate(app);
- if (lastModified) {
- DOWNLOADS_CACHE[url] = {
- lastModified,
+ if (isUrl && currentModified) {
+ DOWNLOADS_CACHE.set(url, {
+ lastModified: currentModified,
fullPath: newApp,
- };
- } else {
- logger.info(`Skipping '${app}' caching, because the HEAD response didn't return` +
- ` any valid Last-Modified headers`);
- }
+ });
}
return newApp;
}
-async function downloadApp (app, appExt) {
+async function downloadApp (app, targetPath) {
let appUrl;
try {
appUrl = url.parse(app);
@@ -139,168 +160,92 @@ async function downloadApp (app, appExt) {
throw new Error(`Invalid App URL (${app})`);
}
- // check if this is zipped
- let isZipFile = _.includes(ZIP_EXTS, path.extname(appUrl.pathname));
- appExt = isZipFile ? '.zip' : appExt;
-
- let downloadedApp;
try {
- downloadedApp = await downloadFile(url.format(appUrl), appExt);
- } catch (err) {
- throw new Error(`Problem downloading app from url ${app}: ${err}`);
- }
-
- return downloadedApp;
-}
-
-async function downloadFile (sourceUrl, suffix) {
- // We will be downloading the files to a directory, so make sure it's there
- // This step is not required if you have manually created the directory
- let targetPath = await tempDir.path({prefix: 'appium-app', suffix});
- let contentType;
-
// don't use request-promise here, we need streams
await new B((resolve, reject) => {
- request(sourceUrl)
+ request(appUrl.href)
.on('error', reject) // handle real errors, like connection errors
- .on('response', function (res) {
+ .on('response', (res) => {
// handle responses that fail, like 404s
if (res.statusCode >= 400) {
return reject(`Error downloading file: ${res.statusCode}`);
}
- contentType = res.headers['content-type'];
})
.pipe(_fs.createWriteStream(targetPath))
- .on('error', reject)
.on('close', resolve);
});
-
- logger.debug(`'${sourceUrl}' downloaded to '${targetPath}'`);
- logger.debug(`Downloaded file type '${contentType}'`);
- return {targetPath, contentType};
+ logger.debug(`'${appUrl.href}' has been downloaded to '${targetPath}'`);
+ return targetPath;
+ } catch (err) {
+ throw new Error(`Problem downloading app from url ${appUrl.href}: ${err.message}`);
}
-
-async function copyLocalZip (localZipPath) {
- logger.debug('Copying local zip to tmp dir');
- if (!(await fs.exists(localZipPath))) {
- throw new Error('Local zip did not exist');
- }
- let fileInfo = await tempDir.open({prefix: 'appium-app', suffix: '.zip'});
- let infile = _fs.createReadStream(localZipPath);
- let outfile = _fs.createWriteStream(fileInfo.path);
- return new B((resolve, reject) => {
- infile.pipe(outfile).on('close', () => {
- resolve(fileInfo.path);
- }).on('error', (err) => { // eslint-disable-line promise/prefer-await-to-callbacks
- reject(err);
- });
- });
}
-async function unzipApp (zipPath, appExt) {
- // first delete any existing apps that might be in our tmp dir
- let {stdout} = await exec('find', [path.dirname(zipPath), '-type', 'd', '-name', `*${appExt}`]);
- for (let line of stdout.trim().split('\n').filter(Boolean)) {
- await fs.rimraf(line);
- }
- // now delete any existing zip payload
- await fs.rimraf(path.resolve(path.dirname(zipPath), 'Payload*'));
- let output = await unzipFile(zipPath);
- let relaxedRegStr = `(?:creating|inflating|extracting): (.+${appExt})/?`;
- // in the strict regex, we check for an entry which ends with the
- // extension
- let strictReg = new RegExp(`${relaxedRegStr}$`, 'm');
- // otherwise, we allow an entry which contains the extension, but we
- // need to be careful, because it might be a false positive
- let relaxedReg = new RegExp(relaxedRegStr, 'm');
- let strictMatch = strictReg.exec(output);
- let relaxedMatch = relaxedReg.exec(output);
- let getAppPath = function (match) {
- return path.resolve(path.dirname(zipPath), match[1]);
- };
-
- if (strictMatch) {
- return getAppPath(strictMatch);
+async function walkDir (dir) {
+ const result = [];
+ for (const name of await fs.readdir(dir)) {
+ const currentPath = path.join(dir, name);
+ result.push(currentPath);
+ if ((await fs.stat(currentPath)).isDirectory()) {
+ result.push(...(await walkDir(currentPath)));
}
-
- if (relaxedMatch) {
- logger.debug('Got a relaxed match for app in zip, be careful for app match errors');
- return getAppPath(relaxedMatch);
}
-
- throw new Error(`App zip unzipped OK, but we could not find a ${appExt} bundle ` +
- `in it. Make sure your archive contains the ${appExt} package ` +
- `and nothing else`);
+ return result;
}
-async function unzipFile (zipPath) {
- logger.debug(`Unzipping ${zipPath}`);
- let valid = await testZipArchive(zipPath);
- if (!valid) {
- throw new Error(`Zip archive ${zipPath} did not test valid`);
- }
+async function unzipApp (zipPath, dstRoot, supportedAppExtensions) {
+ await zip.assertValidZip(zipPath);
- if (system.isWindows()) {
- await zip.extractAllTo(zipPath, path.dirname(zipPath));
- logger.debug('Unzip successful');
- return;
+ if (!_.isArray(supportedAppExtensions)) {
+ supportedAppExtensions = [supportedAppExtensions];
}
- let execEnv = _.clone(process.env);
- delete execEnv.UNZIP;
- let execOpts = {cwd: path.dirname(zipPath), env: execEnv};
+ const tmpRoot = await tempDir.openDir();
try {
- let {stdout} = await exec('unzip', ['-o', zipPath], execOpts);
- return stdout;
- } catch (err) {
- logger.error(`Unzip threw error ${err}`);
- logger.error(`Stderr: ${err.stderr}`);
- logger.error(`Stdout: ${err.stdout}`);
- throw new Error('Archive could not be unzipped, check appium logs.');
- }
-}
-
-async function testZipArchive (zipPath) {
- logger.debug(`Testing zip archive: ${zipPath}`);
- if (system.isWindows()) {
- if (await fs.exists(zipPath)) {
- logger.debug('Zip archive tested clean');
- return true;
- } else {
- logger.debug('Zip archive not found');
- return false;
+ logger.debug(`Unzipping '${zipPath}'`);
+ await zip.extractAllTo(zipPath, tmpRoot);
+ const allExtractedItems = await walkDir(tmpRoot);
+ logger.debug(`Extracted ${allExtractedItems.length} item(s) from '${zipPath}'`);
+ const isSupportedAppItem = (relativePath) => supportedAppExtensions.includes(path.extname(relativePath))
+ || _.some(supportedAppExtensions, (x) => relativePath.includes(`${x}${path.sep}`));
+ const itemsToKeep = allExtractedItems
+ .map((itemPath) => path.relative(tmpRoot, itemPath))
+ .filter((relativePath) => isSupportedAppItem(relativePath))
+ .map((relativePath) => path.resolve(tmpRoot, relativePath));
+ const itemsToRemove = _.difference(allExtractedItems, itemsToKeep)
+ // Avoid parent folders to be recursively removed
+ .filter((itemToRemovePath) => !_.some(itemsToKeep, (itemToKeepPath) => itemToKeepPath.startsWith(itemToRemovePath)));
+ await B.all(itemsToRemove, async (itemPath) => {
+ if (await fs.exists(itemPath)) {
+ await fs.rimraf(itemPath);
}
+ });
+ const allBundleItems = (await walkDir(tmpRoot))
+ .map((itemPath) => path.relative(tmpRoot, itemPath))
+ .filter((relativePath) => isSupportedAppItem(relativePath))
+ // Get the top level match
+ .sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
+ if (_.isEmpty(allBundleItems)) {
+ throw new Error(`App zip unzipped OK, but we could not find ${supportedAppExtensions} bundle(s) ` +
+ `in it. Make sure your archive contains ${supportedAppExtensions} package(s) ` +
+ `and nothing else`);
}
-
- let execEnv = _.clone(process.env);
- delete execEnv.UNZIP;
- let execOpts = {cwd: path.dirname(zipPath), env: execEnv};
- let output;
- try {
- output = await exec('unzip', ['-tq', zipPath], execOpts);
- if (/No errors detected/.exec(output.stdout)) {
- return true;
- }
- logger.error(`Zip file ${zipPath} was not valid`);
- logger.error(`Stderr: ${output.stderr}`);
- logger.error(`Stdout: ${output.stdout}`);
- logger.error('Zip archive did not test successfully, check appium server ' +
- 'logs for output');
- return false;
- } catch (err) {
- logger.error(`Test zip archive threw error ${err}`);
- logger.error(`Stderr: ${err.stderr}`);
- logger.error(`Stdout: ${err.stdout}`);
- throw new Error('Error testing zip archive, are you sure this is a zip file?');
+ const matchedBundle = _.first(allBundleItems);
+ logger.debug(`Matched ${allBundleItems.length} item(s) in the extracted archive. ` +
+ `Assuming '${matchedBundle}' is the correct bundle`);
+ await fs.mv(path.resolve(tmpRoot, matchedBundle), path.resolve(dstRoot, matchedBundle), {
+ mkdirp: true
+ });
+ return path.resolve(dstRoot, matchedBundle);
+ } finally {
+ await fs.rimraf(tmpRoot);
}
}
-async function copyFromWindowsNetworkShare (app, appExt, mountRoot, windowsUserName, windowsPassword) {
- if (system.isWindows()) {
- return await copyLocallyFromWindowsShare(app, appExt);
- } else {
- return await mountWindowsShareOnMac(app, mountRoot, windowsUserName, windowsPassword);
- }
+async function copyFromWindowsNetworkShare (app, mountRoot, windowsUserName, windowsPassword) {
+ return system.isWindows()
+ ? await copyLocallyFromWindowsShare(app)
+ : await mountWindowsShareOnMac(app, mountRoot, windowsUserName, windowsPassword);
}
async function mountWindowsShareOnMac (app, mountRoot, windowsUserName, windowsPassword) {
@@ -337,8 +282,11 @@ async function mountWindowsShareOnMac (app, mountRoot, windowsUserName, windowsP
return app;
}
-async function copyLocallyFromWindowsShare (app, appExt) {
- let fileInfo = await tempDir.open({prefix: 'appium-app', suffix: appExt});
+async function copyLocallyFromWindowsShare (app) {
+ const fileInfo = await tempDir.open({
+ prefix: path.basename(app),
+ suffix: ''
+ });
return await fs.copyFile(app, fileInfo.path);
}
@@ -368,7 +316,6 @@ function getSwipeTouchDuration (waitGesture) {
return duration;
}
-
/**
* Recursively find all instances of the key 'inKey' and rename them 'outKey'
* @param {*} input Any type of input
@@ -389,7 +336,5 @@ function renameKey (input, inKey, outKey) {
}
export {
- configureApp, downloadApp, downloadFile, copyLocalZip, unzipApp, unzipFile,
- testZipArchive, isPackageOrBundle, getCoordDefault, getSwipeTouchDuration,
- copyFromWindowsNetworkShare, renameKey,
+ configureApp, isPackageOrBundle, getCoordDefault, getSwipeTouchDuration, renameKey,
};

MODIFY
package.json
package.json
],
"dependencies": {
"@babel/runtime": "^7.0.0",
- "appium-support": "^2.19.0",
+ "appium-support": "^2.24.0",
"asyncbox": "^2.3.1",
"bluebird": "^2.10.2",
"body-parser": "^1.18.2",

MODIFY
test/basedriver/helpers-e2e-specs.js
test/basedriver/helpers-e2e-specs.js
@@ -2,7 +2,7 @@ import chai from 'chai';
import path from 'path';
import chaiAsPromised from 'chai-as-promised';
import { fs } from 'appium-support';
-import h from '../../lib/basedriver/helpers';
+import { configureApp } from '../../lib/basedriver/helpers';
import http from 'http';
import finalhandler from 'finalhandler';
import serveStatic from 'serve-static';
@@ -14,47 +14,46 @@ chai.should();
chai.use(chaiAsPromised);
function getFixture (file) {
- return path.resolve(__dirname, '..', '..', '..', 'test', 'basedriver',
- 'fixtures', file);
+ return path.resolve(__dirname, '..', '..', '..', 'test', 'basedriver', 'fixtures', file);
}
describe('app download and configuration', function () {
describe('configureApp', function () {
it('should get the path for a local .app', async function () {
- let newAppPath = await h.configureApp(getFixture('FakeIOSApp.app'), '.app');
+ let newAppPath = await configureApp(getFixture('FakeIOSApp.app'), '.app');
newAppPath.should.contain('FakeIOSApp.app');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an app\n');
});
it('should get the path for a local .apk', async function () {
- let newAppPath = await h.configureApp(getFixture('FakeAndroidApp.apk'), '.apk');
+ let newAppPath = await configureApp(getFixture('FakeAndroidApp.apk'), '.apk');
newAppPath.should.contain('FakeAndroidApp.apk');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an apk\n');
});
it('should unzip and get the path for a local .app.zip', async function () {
- let newAppPath = await h.configureApp(getFixture('FakeIOSApp.app.zip'), '.app');
+ let newAppPath = await configureApp(getFixture('FakeIOSApp.app.zip'), '.app');
newAppPath.should.contain('FakeIOSApp.app');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an app\n');
});
it('should unzip and get the path for a local .ipa', async function () {
- let newAppPath = await h.configureApp(getFixture('FakeIOSApp.ipa'), '.app');
+ let newAppPath = await configureApp(getFixture('FakeIOSApp.ipa'), '.app');
newAppPath.should.contain('FakeIOSApp.app');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an app\n');
});
it('should fail for a bad zip file', async function () {
- await h.configureApp(getFixture('BadZippedApp.zip'), '.app')
- .should.be.rejectedWith('Error testing zip archive, are you sure this is a zip file?');
+ await configureApp(getFixture('BadZippedApp.zip'), '.app')
+ .should.be.rejectedWith(/PK/);
});
it('should fail if extensions do not match', async function () {
- await h.configureApp(getFixture('FakeIOSApp.app'), '.wrong')
- .should.be.rejectedWith(/did not have extension '.wrong'/);
+ await configureApp(getFixture('FakeIOSApp.app'), '.wrong')
+ .should.be.rejectedWith(/did not have extension/);
});
it('should fail if zip file does not contain an app whose extension matches', async function () {
- await h.configureApp(getFixture('FakeIOSApp.app.zip'), '.wrong')
- .should.be.rejectedWith(/could not find a .wrong bundle in it/);
+ await configureApp(getFixture('FakeIOSApp.app.zip'), '.wrong')
+ .should.be.rejectedWith(/could not find/);
});
describe('should download an app from the web', function () {
const port = 8000;
@@ -62,7 +61,7 @@ describe('app download and configuration', function () {
describe('server not available', function () {
it('should handle server not available', async function () {
- await h.configureApp(`${serverUrl}/FakeIOSApp.app.zip`, '.app')
+ await configureApp(`${serverUrl}/FakeIOSApp.app.zip`, '.app')
.should.eventually.be.rejectedWith(/ECONNREFUSED/);
});
});
@@ -70,8 +69,7 @@ describe('app download and configuration', function () {
// use a local server so there is no dependency on the internet
let server;
before(function () {
- const dir = path.resolve(__dirname, '..', '..', '..', 'test',
- 'basedriver', 'fixtures');
+ const dir = path.resolve(__dirname, '..', '..', '..', 'test', 'basedriver', 'fixtures');
const serve = serveStatic(dir, {
index: false,
setHeaders: (res, path) => {
@@ -111,59 +109,65 @@ describe('app download and configuration', function () {
});
it('should download zip file', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeIOSApp.app.zip`, '.app');
+ let newAppPath = await configureApp(`${serverUrl}/FakeIOSApp.app.zip`, '.app');
newAppPath.should.contain('FakeIOSApp.app');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an app\n');
});
it('should download zip file with query string', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeIOSApp.app.zip?sv=abc&sr=def`, '.app');
+ let newAppPath = await configureApp(`${serverUrl}/FakeIOSApp.app.zip?sv=abc&sr=def`, '.app');
newAppPath.should.contain('.app');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an app\n');
});
it('should download an app file', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeIOSApp.app`, '.app');
+ let newAppPath = await configureApp(`${serverUrl}/FakeIOSApp.app`, '.app');
newAppPath.should.contain('.app');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an app\n');
});
+ it('should accept multiple extensions', async function () {
+ let newAppPath = await configureApp(`${serverUrl}/FakeIOSApp.app.zip`, ['.app', '.aab']);
+ newAppPath.should.contain('FakeIOSApp.app');
+ let contents = await fs.readFile(newAppPath, 'utf8');
+ contents.should.eql('this is not really an app\n');
+ });
it('should download an apk file', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeAndroidApp.apk`, '.apk');
+ let newAppPath = await configureApp(`${serverUrl}/FakeAndroidApp.apk`, '.apk');
newAppPath.should.contain('.apk');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an apk\n');
});
it('should handle zip file that cannot be downloaded', async function () {
- await h.configureApp(`${serverUrl}/missing/FakeIOSApp.app.zip`, '.app')
+ await configureApp(`${serverUrl}/missing/FakeIOSApp.app.zip`, '.app')
.should.eventually.be.rejectedWith(/Problem downloading app from url/);
});
it('should handle invalid protocol', async function () {
- await h.configureApp('file://C:/missing/FakeIOSApp.app.zip', '.app')
+ await configureApp('file://C:/missing/FakeIOSApp.app.zip', '.app')
.should.eventually.be.rejectedWith(/is not supported/);
- await h.configureApp('ftp://localhost:8000/missing/FakeIOSApp.app.zip', '.app')
+ await configureApp('ftp://localhost:8000/missing/FakeIOSApp.app.zip', '.app')
.should.eventually.be.rejectedWith(/is not supported/);
});
it('should handle missing file in Windows path format', async function () {
- await h.configureApp('C:\\missing\\FakeIOSApp.app.zip', '.app')
+ await configureApp('C:\\missing\\FakeIOSApp.app.zip', '.app')
.should.eventually.be.rejectedWith(/does not exist or is not accessible/);
});
it('should recognize zip mime types and unzip the downloaded file', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeAndroidApp.asd?mime-zip`, '.apk');
+ let newAppPath = await configureApp(`${serverUrl}/FakeAndroidApp.asd?mime-zip`, '.apk');
newAppPath.should.contain('FakeAndroidApp.apk');
newAppPath.should.not.contain('.asd');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an apk\n');
});
it('should recognize zip mime types and unzip the downloaded file with query string', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeAndroidApp.asd?mime-zip&sv=abc&sr=def`, '.apk');
+ let newAppPath = await configureApp(`${serverUrl}/FakeAndroidApp.asd?mime-zip&sv=abc&sr=def`, '.apk');
newAppPath.should.contain('FakeAndroidApp.apk');
newAppPath.should.not.contain('.asd');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an apk\n');
});
it('should treat an unknown mime type as an app', async function () {
- let newAppPath = await h.configureApp(`${serverUrl}/FakeAndroidApp.apk?mime-bip`, '.apk');
+ let newAppPath = await configureApp(`${serverUrl}/FakeAndroidApp.apk?mime-bip`, '.apk');
newAppPath.should.contain('.apk');
let contents = await fs.readFile(newAppPath, 'utf8');
contents.should.eql('this is not really an apk\n');

MODIFY
test/basedriver/helpers-specs.js
test/basedriver/helpers-specs.js
-import { isPackageOrBundle, unzipFile, renameKey } from '../../lib/basedriver/helpers';
+import { isPackageOrBundle, renameKey } from '../../lib/basedriver/helpers';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
-import path from 'path';
-import sinon from 'sinon';
-import { system, fs } from 'appium-support';
-import mockFS from 'mock-fs';
+
chai.use(chaiAsPromised);
const should = chai.should();
@@ -21,32 +18,6 @@ describe('helpers', function () {
});
});
- describe('#unzipFile', function () {
- let mockDir = 'path/to/mock/dir';
- before(async function () {
- // Start mock filesystem
- mockFS({
- [mockDir]: {},
- });
-
- // Write the base64 contents of FakeIOSApp.app.zip to the mock filesystem
-
- const fakeIOSAppZip = 'UEsDBBQACAAIABF8/EYAAAAAAAAAAAAAAAAOABAARmFrZUlPU0FwcC5hcHBVWAwALwO4VQIDuFX1ARQAK8nILFYAorz8EoWi1MScnEqFxDyFxIICLgBQSwcIR93jPhoAAAAaAAAAUEsBAhUDFAAIAAgAEXz8Rkfd4z4aAAAAGgAAAA4ADAAAAAAAAAAAQKSBAAAAAEZha2VJT1NBcHAuYXBwVVgIAC8DuFUCA7hVUEsFBgAAAAABAAEASAAAAGYAAAAAAA==';
- await fs.writeFile(path.resolve(mockDir, 'FakeIOSApp.app.zip'), fakeIOSAppZip, 'base64');
- });
-
- after(function () {
- mockFS.restore();
- });
-
- it('should unzip a .zip file (force isWindows to be true so we can test the internal zip library)', async function () {
- const forceWindows = sinon.stub(system, 'isWindows').returns(true);
- await unzipFile(path.resolve(mockDir, 'FakeIOSApp.app.zip'));
- await fs.readFile(path.resolve(mockDir, 'FakeIOSApp.app'), 'utf8').should.eventually.deep.equal('this is not really an app\n');
- forceWindows.restore();
- });
- });
-
describe('#renameKey', function () {
it('should translate key in an object', function () {
renameKey({'foo': 'hello world'}, 'foo', 'bar').should.eql({'bar': 'hello world'});

Make it possible to pass multiple supported extensions to configureApp helper (#271)