How to use remoteMkdirp method in Appium Xcuitest Driver

Best JavaScript code snippet using appium-xcuitest-driver

Run Appium Xcuitest Driver automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Sign up Free
_

ios-fs-helpers.js

Source: ios-fs-helpers.js Github

copy
1import _ from 'lodash';
2import B from 'bluebird';
3import { fs, tempDir, mkdirp, zip, util, timing } from '@appium/support';
4import path from 'path';
5import log from './logger';
6
7const IO_TIMEOUT_MS = 4 * 60 * 1000;
8// Mobile devices use NAND memory modules for the storage,
9// and the parallelism there is not as performant as on regular SSDs
10const MAX_IO_CHUNK_SIZE = 8;
11
12/**
13 * Retrieve a file from a real device
14 *
15 * @param {AfcService} afcService Apple File Client service instance from
16 * 'appium-ios-device' module
17 * @param {string} remotePath Relative path to the file on the device
18 * @returns {Buffer} The file content as a buffer
19 */
20async function pullFile (afcService, remotePath) {
21  const stream = await afcService.createReadStream(remotePath, { autoDestroy: true });
22  const pullPromise = new B((resolve, reject) => {
23    stream.on('close', resolve);
24    stream.on('error', reject);
25  }).timeout(IO_TIMEOUT_MS);
26  const buffers = [];
27  stream.on('data', (data) => buffers.push(data));
28  await pullPromise;
29  return Buffer.concat(buffers);
30}
31
32/**
33 * Checks a presence of a local folder.
34 *
35 * @param {string} folderPath Full path to the local folder
36 * @returns {boolean} True if the folder exists and is actually a folder
37 */
38async function folderExists (folderPath) {
39  try {
40    return (await fs.stat(folderPath)).isDirectory();
41  } catch (e) {
42    return false;
43  }
44}
45
46/**
47 * Retrieve a folder from a real device
48 *
49 * @param {AfcService} afcService Apple File Client service instance from
50 * 'appium-ios-device' module
51 * @param {string} remoteRootPath Relative path to the folder on the device
52 * @returns {Buffer} The folder content as a zipped base64-encoded buffer
53 */
54async function pullFolder (afcService, remoteRootPath) {
55  const tmpFolder = await tempDir.openDir();
56  try {
57    let localTopItem = null;
58    let countFilesSuccess = 0;
59    let countFilesFail = 0;
60    let countFolders = 0;
61    const pullPromises = [];
62    await afcService.walkDir(remoteRootPath, true, async (remotePath, isDir) => {
63      const localPath = path.join(tmpFolder, remotePath);
64      const dirname = isDir ? localPath : path.dirname(localPath);
65      if (!await folderExists(dirname)) {
66        await mkdirp(dirname);
67      }
68      if (!localTopItem
69          || localPath.split(path.sep).length < localTopItem.split(path.sep).length) {
70        localTopItem = localPath;
71      }
72      if (isDir) {
73        ++countFolders;
74        return;
75      }
76
77      const readStream = await afcService.createReadStream(remotePath, {autoDestroy: true});
78      const writeStream = fs.createWriteStream(localPath, {autoClose: true});
79      pullPromises.push(
80        new B((resolve) => {
81          writeStream.on('close', () => {
82            ++countFilesSuccess;
83            resolve();
84          });
85          const onStreamingError = (e) => {
86            readStream.unpipe(writeStream);
87            log.warn(`Cannot pull '${remotePath}' to '${localPath}'. ` +
88              `The file will be skipped. Original error: ${e.message}`);
89            ++countFilesFail;
90            resolve();
91          };
92          writeStream.on('error', onStreamingError);
93          readStream.on('error', onStreamingError);
94        }).timeout(IO_TIMEOUT_MS)
95      );
96      readStream.pipe(writeStream);
97      if (pullPromises.length >= MAX_IO_CHUNK_SIZE) {
98        await B.any(pullPromises);
99      }
100      _.remove(pullPromises, (p) => p.isFulfilled());
101    });
102    // Wait for the rest of files to be pulled
103    if (!_.isEmpty(pullPromises)) {
104      await B.all(pullPromises);
105    }
106    log.info(`Pulled ${util.pluralize('file', countFilesSuccess, true)} out of ` +
107      `${countFilesSuccess + countFilesFail} and ${util.pluralize('folder', countFolders, true)} ` +
108      `from '${remoteRootPath}'`);
109    return await zip.toInMemoryZip(localTopItem ? path.dirname(localTopItem) : tmpFolder, {
110      encodeToBase64: true,
111    });
112  } finally {
113    await fs.rimraf(tmpFolder);
114  }
115}
116
117/**
118 * Creates remote folder path recursively. Noop if the given path
119 * already exists
120 *
121 * @param {AfcService} afcService Apple File Client service instance from
122 * 'appium-ios-device' module
123 * @param {string} remoteRoot The relative path to the remote folder structure
124 * to be created
125 */
126async function remoteMkdirp (afcService, remoteRoot) {
127  if (remoteRoot === '.' || remoteRoot === '/') {
128    return;
129  }
130  try {
131    await afcService.listDirectory(remoteRoot);
132    return;
133  } catch (e) {
134    // This means that the directory is missing and we got an object not found error.
135    // Therefore, we are going to the parent
136    await remoteMkdirp(afcService, path.dirname(remoteRoot));
137  }
138  await afcService.createDirectory(remoteRoot);
139}
140
141/**
142 * Pushes a file to a real device
143 *
144 * @param {AfcService} afcService Apple File Client service instance from
145 * 'appium-ios-device' module
146 * @param {string} remotePath Relative path to the file on the device. The remote
147 * folder structure is created automatically if necessary.
148 * @param {string} base64Data Base64-encoded content of the file to be written
149 */
150async function pushFile (afcService, remotePath, base64Data) {
151  await remoteMkdirp(afcService, path.dirname(remotePath));
152  const stream = await afcService.createWriteStream(remotePath, {autoDestroy: true});
153  let pushError = null;
154  const pushPromise = new B((resolve, reject) => {
155    stream.on('error', (e) => {
156      pushError = e;
157    });
158    stream.on('close', () => {
159      if (pushError) {
160        reject(pushError);
161      } else {
162        resolve();
163      }
164    });
165  }).timeout(IO_TIMEOUT_MS);
166  stream.write(Buffer.from(base64Data, 'base64'));
167  stream.end();
168  await pushPromise;
169}
170
171/**
172 * @typedef {Object} PushFolderOptions
173 *
174 * @property {number} timeoutMs [240000] The maximum timeout to wait until a
175 * single file is copied
176 * @param {boolean} enableParallelPush [false] Whether to push files in parallel.
177 * This usually gives better performance, but might sometimes be less stable.
178 */
179
180/**
181 * Pushes a folder to a real device
182 *
183 * @param {AfcService} afcService Apple File Client service instance from
184 * 'appium-ios-device' module
185 * @param {string} srcRootPath The full path to the source folder
186 * @param {string} dstRootPath The relative path to the destination folder. The folder
187 * will be deleted if already exists.
188 * @param {PushFolderOptions} opts
189 */
190async function pushFolder (afcService, srcRootPath, dstRootPath, opts = {}) {
191  const {
192    timeoutMs = IO_TIMEOUT_MS,
193    enableParallelPush = false,
194  } = opts;
195
196  const timer = new timing.Timer().start();
197  const itemsToPush = await fs.glob('**', {
198    cwd: srcRootPath,
199    nosort: true,
200    mark: true,
201  });
202  log.debug(`Successfully scanned the tree structure of '${srcRootPath}'`);
203  const [foldersToPush, filesToPush] = itemsToPush.reduce((acc, x) => {
204    acc[_.endsWith(x, path.sep) ? 0 : 1].push(x);
205    return acc;
206  }, [[], []]);
207  log.debug(`Got ${util.pluralize('folder', foldersToPush.length, true)} and ` +
208    `${util.pluralize('file', filesToPush.length, true)} to push`);
209  // create the folder structure first
210  try {
211    await afcService.deleteDirectory(dstRootPath);
212  } catch (ign) {}
213  await afcService.createDirectory(dstRootPath);
214  // top-level folders must go first
215  const foldersToPushByHierarchy = foldersToPush
216    .sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
217  for (const relativeFolderPath of foldersToPushByHierarchy) {
218    // createDirectory does not accept folder names ending with a path separator
219    const absoluteFolderPath = _.trimEnd(
220      path.join(dstRootPath, relativeFolderPath), path.sep
221    );
222    if (absoluteFolderPath) {
223      await afcService.createDirectory(absoluteFolderPath);
224    }
225  }
226  // do not forget about the root folder
227  log.debug(`Successfully created the remote folder structure ` +
228    `(${util.pluralize('item', foldersToPush.length + 1, true)})`);
229
230  const pushFile = async (relativePath) => {
231    const absoluteSourcePath = path.join(srcRootPath, relativePath);
232    const readStream = fs.createReadStream(absoluteSourcePath, {autoClose: true});
233    const absoluteDestinationPath = path.join(dstRootPath, relativePath);
234    const writeStream = await afcService.createWriteStream(absoluteDestinationPath, {
235      autoDestroy: true
236    });
237    writeStream.on('finish', writeStream.destroy);
238    let pushError = null;
239    const filePushPromise = new B((resolve, reject) => {
240      writeStream.on('close', () => {
241        if (pushError) {
242          reject(pushError);
243        } else {
244          resolve();
245        }
246      });
247      const onStreamError = (e) => {
248        readStream.unpipe(writeStream);
249        log.debug(e);
250        pushError = e;
251      };
252      writeStream.on('error', onStreamError);
253      readStream.on('error', onStreamError);
254    });
255    readStream.pipe(writeStream);
256    await filePushPromise.timeout(timeoutMs);
257  };
258
259  if (enableParallelPush) {
260    log.debug(`Proceeding to parallel files push (max ${MAX_IO_CHUNK_SIZE} writers)`);
261    const pushPromises = [];
262    for (const relativeFilePath of _.shuffle(filesToPush)) {
263      pushPromises.push(B.resolve(pushFile(relativeFilePath)));
264      // keep the push queue filled
265      if (pushPromises.length >= MAX_IO_CHUNK_SIZE) {
266        await B.any(pushPromises);
267      }
268      _.remove(pushPromises, (p) => p.isFulfilled());
269    }
270    if (!_.isEmpty(pushPromises)) {
271      // handle the rest of push promises
272      await B.all(pushPromises);
273    }
274  } else {
275    log.debug(`Proceeding to serial files push`);
276    for (const relativeFilePath of filesToPush) {
277      await pushFile(relativeFilePath);
278    }
279  }
280
281  log.debug(`Successfully pushed ${util.pluralize('folder', foldersToPush.length, true)} ` +
282    `and ${util.pluralize('file', filesToPush.length, true)} ` +
283    `within ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
284}
285
286
287export { pullFile, pullFolder, pushFile, pushFolder };
Full Screen

fs-rsync.js

Source: fs-rsync.js Github

copy
1(function(definition) {
2  if (typeof module !== 'undefined') {
3    // CommonJS
4    module.exports = definition(require('fs-rpc'));
5  }
6  else if (typeof define === 'function' && typeof define.amd === 'object') {
7    // AMD
8    define(['fs-rpc'], definition);
9  }
10  else if (typeof window === 'object') {
11    // DOM
12    window.FSRSYNC = definition(window.FSRPC());
13  }
14}(function (FSRPC) {
15
16  'use strict';
17
18  var RPC = FSRPC.Client;
19    
20  var FSRSYNC = function (localFs, connection, urlPathname) {
21
22    var self = this,
23      fnDone = localFs.fnDone;
24
25    this.localFs = localFs;
26    this.connection = connection;
27    this.urlPathname = urlPathname || 'rpc';
28    this.deletedLocalFiles = [];
29    this.renamedLocalFiles = [];
30
31    if ('function' === typeof fnDone) {
32      localFs.fnDone = function () {
33
34        var fnName = arguments[0],
35          info = arguments[1];
36        
37        if (-1 !== ['unlink', 'rmdir', 'rmrf'].indexOf(fnName)) {
38          self.deletedLocalFiles.push(localFs.normalizePath(info));
39        }
40        else if ('rename' === fnName) {
41          // info.newPath, info.oldPath
42          self.renamedLocalFiles.push([
43            localFs.normalizePath(info.oldPath),
44            localFs.normalizePath(info.newPath)
45          ]);
46        }
47        
48        // console.log(fnName, info);
49        fnDone.apply(localFs, arguments);
50      };
51    }
52  };
53
54  var base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
55
56  var base64CharCodeMap = (function () {
57      var map = {},
58        charCode,
59        i = 0,
60        to = base64Chars.length;
61
62      while (i < to){
63        charCode = base64Chars.charCodeAt(i);
64        if (charCode) {
65          map[charCode] = i;          
66        }
67        ++i;
68      }
69      return map;
70    })();
71
72
73  FSRSYNC.arrayBufferToBase64 = function (arrayBuffer) {
74
75    var bytes = new Uint8Array(arrayBuffer),
76      i = 0, 
77      len = bytes.length, 
78      base64 = '';
79
80    for (; i < len; i+=3) {
81      base64 += base64Chars[bytes[i] >> 2];
82      base64 += base64Chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
83      base64 += base64Chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
84      base64 += base64Chars[bytes[i + 2] & 63];
85    }
86
87    if ((len % 3) === 2) {
88      base64 = base64.substring(0, base64.length - 1) + '=';
89    } else if (len % 3 === 1) {
90      base64 = base64.substring(0, base64.length - 2) + '==';
91    }
92
93    return base64;
94  };  // arrayBufferToBase64
95
96
97  FSRSYNC.base64ToArrayBuffer = function (base64Str) {
98
99    var strLenght = base64Str.length, 
100      bufferLength = base64Str.length * 0.75,
101      arraybuffer,
102      bytes,
103      stringIndex = 0, 
104      byteIndex = 0,
105      byte0, 
106      byte1, 
107      byte2, 
108      byte3;
109
110    if (base64Str[strLenght - 1] === '=') {
111      bufferLength--;
112      if (base64Str[strLenght - 2] === '=') {
113        bufferLength--;
114      }
115    }
116
117    arraybuffer = new ArrayBuffer(bufferLength);
118    bytes = new Uint8Array(arraybuffer);
119
120    for (; stringIndex < strLenght; stringIndex += 4) {
121      byte0 = base64CharCodeMap[base64Str.charCodeAt(stringIndex)] || 0;
122      byte1 = base64CharCodeMap[base64Str.charCodeAt(stringIndex + 1) || 0];
123      byte2 = base64CharCodeMap[base64Str.charCodeAt(stringIndex + 2) || 0];
124      byte3 = base64CharCodeMap[base64Str.charCodeAt(stringIndex + 3) || 0];
125
126      bytes[byteIndex++] = (byte0 << 2) | (byte1 >> 4);
127      bytes[byteIndex++] = ((byte1 & 15) << 4) | (byte2 >> 2);
128      bytes[byteIndex++] = ((byte2 & 3) << 6) | (byte3 & 63);
129    }
130
131    return arraybuffer;
132  };  // base64ToArrayBuffer
133
134
135  // get the url path name for connection.send()
136  FSRSYNC.prototype.getUrlPathname = function () {
137    return this.urlPathname;
138  };
139
140  // list remote dir content and stats
141  FSRSYNC.prototype.remoteList = function (pathname, callback) {
142
143    this.connection.send(
144      RPC.stringify('readdirStat', pathname), 
145      this.getUrlPathname(),
146      function (err, result) {     
147        if (err) { return callback(err); }
148        callback.apply(null, RPC.parse(result));
149      }
150    );
151
152  };  // FSRSYNC.remoteList
153
154
155  FSRSYNC.prototype.remoteStat = function (filename, callback) {
156    this.connection.send(
157      RPC.stringify('stat', filename), 
158      this.getUrlPathname(),
159      function (err, result) {
160        if (err) { return callback(err); }
161        callback.apply(null, RPC.parse(result));
162      }
163    );    
164  };  // FSRSYNC.remoteStat
165
166
167  FSRSYNC.prototype.remoteMkdir = function (pathname, callback) {
168    var self = this;
169    this.connection.send(
170      // filename, data, options, callback
171      RPC.stringify('mkdir', [pathname]), 
172      this.getUrlPathname(),
173      function (err, result) {
174
175        var parsed;
176
177        if (err) {
178          return callback(err);  
179        }
180
181        parsed = RPC.parse(result);
182
183        if (parsed[0] instanceof Error) {
184          callback.apply(null, parsed);
185          return;
186        }
187
188        // return remote stats with callback
189        self.remoteStat(pathname, callback);
190      }
191    );    
192  };  // FSRSYNC.remoteMkdir
193
194
195  FSRSYNC.prototype.remoteMkdirp = function (pathname, callback) {
196    var self = this;
197    this.connection.send(
198      // filename, data, options, callback
199      RPC.stringify('mkdirp', [pathname]), 
200      this.getUrlPathname(),
201      function (err, result) {
202        
203        var parsed;
204
205        if (err) {
206          return callback(err);  
207        }
208
209        parsed = RPC.parse(result);
210
211        if (parsed[0] instanceof Error) {
212          callback.apply(null, parsed);
213          return;
214        }
215
216        // return remote stats with callback
217        self.remoteStat(pathname, callback);
218      }
219    );    
220  };  // FSRSYNC.remoteMkdir
221
222
223  FSRSYNC.prototype.remoteWriteFile = function (filename, data, options, callback) {
224
225    var self = this,
226      chunkSize,
227      chunksToSend,
228      optionsWriteFile = {};
229
230    if ('undefined' === typeof callback) {
231      callback = options;
232      options = {};
233    }
234
235    options = options || {};
236
237    chunkSize = options.chunkSize || 1024 * 128;
238    chunksToSend = Math.ceil(data.byteLength / chunkSize);
239
240    function writeFileChunked (chunk) {
241
242      var start = (chunk - 1) * chunkSize,
243        end = start + chunkSize, 
244        bufferChunk = data.slice(start, end),
245        base64Data = FSRSYNC.arrayBufferToBase64(bufferChunk);
246
247      // console.log('writeFileChunked ', chunk, ' of ' + chunksToSend);
248
249      optionsWriteFile.chunk = chunk;
250      optionsWriteFile.chunks = chunksToSend;
251
252      self.connection.send(
253        
254        // filename, data, options, callback
255        RPC.stringify('writeFileChunked', [filename, base64Data, optionsWriteFile]), 
256        
257        self.getUrlPathname(),
258        
259        function (err, result) {
260
261          var parsed;
262          
263          if (err) {
264            return callback(err);            
265          }
266
267          parsed = RPC.parse(result);
268
269          if (parsed[0] instanceof Error) {
270            callback.apply(null, parsed);
271            return;
272          }
273
274          if (chunk >= chunksToSend) {
275            // return remote stats with callback
276            self.remoteStat(filename, callback);            
277          }
278          else {
279            // write next chunk
280            writeFileChunked(chunk + 1);
281          }
282        }
283      );    
284    } // writeFileChunked
285
286    // write first chunk
287    writeFileChunked(1);
288  };  // FSRSYNC.remoteWriteFile
289
290
291  FSRSYNC.prototype.remoteReadFile = function (filename, options, callback) {
292
293    var self = this,
294      base64FileContent = '',
295      optionsReadFile = {};
296
297    if ('undefined' === typeof callback) {
298      callback = options;
299      options = {};
300    }
301
302    options = options || {};
303
304    optionsReadFile.chunkSize = options.chunkSize = options.chunkSize || 1024 * 128;
305
306    function readFileChunk (chunk) {
307
308      // console.log('readFileChunk', chunk);
309
310      // rpcfs.readFileChunked (filename, options, callback)
311      // options: {chunkSize: 128k, chunk: 2}
312      // callback: (err, result)
313      // result: {chunk: 1, EOF: true, content: 'base64', chunkSize: 128k, stats: {}}
314
315      optionsReadFile.chunk = chunk;
316
317      self.connection.send(
318        RPC.stringify('readFileChunked', [filename, optionsReadFile]), 
319        self.getUrlPathname(),
320        function (err, result) {
321
322          var parsed,
323            readResult,
324            arrayBuffer;
325
326          if (err) { callback(err); return; }
327
328          try {
329            parsed = RPC.parse(result);
330            // console.log('read parsed:', parsed);
331            if (parsed) {
332
333              readResult = parsed[1];
334              
335              if (parsed[0] instanceof Error) {
336                err = readResult[0];
337              }
338              else if (readResult && 'string' === typeof readResult.content) {
339                base64FileContent += readResult.content;
340              }
341            }
342          }
343          catch (e) {
344            err = e;
345          }
346
347          if (err) {
348            callback(err);            
349          }
350          else if (readResult && readResult.EOF) {
351            arrayBuffer = FSRSYNC.base64ToArrayBuffer(base64FileContent);
352            callback(null, arrayBuffer);            
353          }
354          else {
355            // next chunk
356            readFileChunk(chunk + 1);
357          }
358        }
359      );    
360    } // readFileChunk
361
362    readFileChunk(1);
363  };  // FSRSYNC.remoteReadFile
364
365
366  FSRSYNC.prototype.remoteUnlink =  function (filename, callback) {
367    this.connection.send(
368      RPC.stringify('unlink', filename), 
369      this.getUrlPathname(),
370      function (err, result) {
371        if (err) { return callback(err); }
372        callback.apply(null, RPC.parse(result));
373      }
374    );    
375  }; // remoteUnlink
376
377
378  FSRSYNC.prototype.remoteRmrf =  function (pathname, callback) {
379    this.connection.send(
380      RPC.stringify('rmrf', pathname), 
381      this.getUrlPathname(),
382      function (err, result) {
383        if (err) { return callback(err); }
384        callback.apply(null, RPC.parse(result));
385      }
386    );  
387  }; // remoteRmrf
388
389
390  FSRSYNC.prototype.localList = function (pathname) {
391    
392    var self = this,
393      result = {};
394    
395    this.localFs.readdirSync(pathname).forEach(function (filename) {
396      result[filename] = self.localFs.statSync(pathname + '/' + filename);
397    });
398
399    return result;
400  };  // FSRSYNC.localList
401
402
403  FSRSYNC.eachAsync = function (list, fn, callback) {
404
405    var listIndex = 0,
406      listPromise;
407
408    if (!list || 0 === list.length) {
409      callback();
410      return;
411    }
412
413    function allDone (err) {
414      setTimeout(function () {
415        callback(err || null);          
416      }, 0);
417    }
418
419    listPromise = new Promise(function (resolveList, rejectList) {
420
421      var fnPromise;
422
423      function executor (resolve, reject) {     
424        try {
425          fn.call(null, list[listIndex], function (err) {
426            if (err instanceof Error) {
427              reject(err);
428            }
429            else {
430              resolve();
431            }
432          });
433        } 
434        catch(e) {
435          reject(e);
436        }  
437      }
438
439      function fnPromiseResolved () {
440        ++listIndex;
441        if (listIndex < list.length) {
442          fnPromise = new Promise(executor);
443
444          fnPromise.then(
445            fnPromiseResolved,
446            fnPromiseRejected
447          );      
448        }
449        else {
450          resolveList();
451        }
452      }
453
454      function fnPromiseRejected (err) {
455        rejectList(err);
456      }
457
458      fnPromise = new Promise(executor);
459
460      fnPromise.then(
461        fnPromiseResolved,
462        fnPromiseRejected
463      );      
464    });
465
466    listPromise.then(allDone, allDone);
467  };  // FSRSYNC.eachAsync
468
469
470  FSRSYNC.prototype.handleRenamedLocalFiles = function () {
471
472    var fs = this.localFs,
473      list = this.renamedLocalFiles,
474      i;
475    for (i = 0; i < list.length; ++i) {
476      var node, 
477        oldPath = list[i][0],
478        newPath = list[i][1];
479      
480      if (!fs.existsSync(oldPath)) {
481        // add old filename to deleted local files list
482        this.deletedLocalFiles.push(oldPath);
483      }
484
485      if (fs.existsSync(newPath)) {
486        node = fs.getNode(newPath);
487        // remove the remote stats property to make it a "new" file
488        delete node.remoteStats;
489      }
490    }
491
492    list.length = 0;
493    return this;
494  };
495
496
497  FSRSYNC.prototype.syncFile = function (filename, callback) {
498
499    var self = this,
500      existsOnLocal = this.localFs.existsSync(filename);
501
502    this.handleRenamedLocalFiles();
503
504    this.remoteStat(filename, function (err, remoteStats) {
505
506      var deletedLocalFilesIndex;
507
508      if (!err && remoteStats) {
509        // file exists on remote
510        if (existsOnLocal) {
511          // exists both on local and remote fs
512          self.syncExistingFiles(
513            filename,
514            self.localFs.statSync(filename),
515            remoteStats,
516            callback
517          );
518        }
519        else {
520          // remote file does not exist in local fs
521          deletedLocalFilesIndex = self.deletedLocalFiles.indexOf(filename);
522
523          if (-1 !== deletedLocalFilesIndex) {
524            // deleted on local fs, also delete on remote 
525            self.remoteUnlink(
526              filename, 
527              function (err) {
528                if (!err) {
529                  delete self.deletedLocalFiles[deletedLocalFilesIndex];
530                }
531                callback(err);
532              }
533            );
534          }
535          else {
536            // create on local fs
537            self.createLocal(
538              filename,
539              remoteStats,
540              callback
541            );
542          }          
543        }
544      }
545      else {
546        // file not on remote
547        if (existsOnLocal) {
548          // create on remote
549          self.createRemote(
550            filename,
551            self.localFs.statSync(filename),
552            callback
553          );
554        }
555        else {
556          // does not exist anywhere
557          callback(null);
558        }
559      }
560    });
561  };  // syncFile
562
563  
564  FSRSYNC.prototype.syncDir = function (pathname, options, callback) {
565      
566    var self = this,
567      recursive,
568      existsOnLocal;
569
570    if ('undefined' === typeof callback) {
571      callback = options;
572      options = {};
573    }
574
575    recursive = !!options.recursive;
576
577    if ('/' !== pathname[pathname.length - 1]) {
578      pathname = pathname + '/';
579    }
580
581    this.handleRenamedLocalFiles();
582
583    existsOnLocal = this.localFs.existsSync(pathname);
584
585    function loopRemoteFiles (localList, remoteList, loopRemoteFilesCallback) {
586
587      FSRSYNC.eachAsync(
588        Object.keys(remoteList),
589        function (remoteFilename, done) {
590
591          var remoteStats = remoteList[remoteFilename],
592            deletedLocalFilesIndex,
593            filename = self.localFs.normalizePath(pathname + remoteFilename);
594          
595          if (!localList[remoteFilename]) {
596            // remote file does not exist in local fs
597            deletedLocalFilesIndex = self.deletedLocalFiles.indexOf(filename);
598            if (-1 !== deletedLocalFilesIndex) {
599              // deleted on local fs, also delete on remote 
600              self[remoteStats.isDirectory ? 'remoteRmrf' : 'remoteUnlink'](
601                filename, 
602                function (err) {
603                  if (!err) {
604                    delete self.deletedLocalFiles[deletedLocalFilesIndex];
605                  }
606                  done(err);
607                }
608              );
609            }
610            else {
611              // new file created on remote fs 
612              self.createLocal(filename, remoteStats, done);
613            }
614          }
615          else {
616            done();
617          }
618        },
619        loopRemoteFilesCallback
620      );
621    } // loopRemoteFiles
622
623
624    function loopLocalFiles (localList, remoteList, loopLocalFilesCallback) {
625
626      FSRSYNC.eachAsync(
627        Object.keys(localList),
628        function (localFilename, done) {
629
630          var localStats = localList[localFilename],
631            localNode = self.localFs.getNode(pathname + localFilename);
632          
633          if (!remoteList[localFilename]) {
634            // local file is not on remote
635
636            if (localNode.remoteStats) {
637              // file deleted on remote fs 
638              if (localStats.isDirectory()) {
639                self.localFs.rmdir(pathname + localFilename, done);
640              }
641              else {
642                self.localFs.unlink(pathname + localFilename, done);
643              }
644            }
645            else {
646              // new file created on local fs 
647              self.createRemote(
648                pathname + localFilename,
649                localStats, 
650                done
651              );
652            }
653          }
654          else {
655            // local also exists on remote..
656            done();            
657          }
658
659        },
660        loopLocalFilesCallback
661      );
662    } // loopLocalFiles
663
664
665    function handleModifiedFiles (localList, remoteList, handleModifiedFilesCallback) {
666
667      FSRSYNC.eachAsync(
668        Object.keys(remoteList),
669        function (remoteFilename, done) {
670
671          var localStats = localList[remoteFilename],
672            remoteStats = remoteList[remoteFilename],
673            filename = pathname + remoteFilename;
674          
675          if (!localStats || !remoteStats) {
676            return done();
677          }
678
679          if (localStats.isDirectory()) {
680            return done();
681          }
682
683          self.syncExistingFiles(
684            filename,
685            localStats,
686            remoteStats,
687            done
688          );
689
690        },
691        handleModifiedFilesCallback
692      );
693    } // handleModifiedFiles
694
695
696    function handleRemoteList (remoteList, handleRemoteListDone) {
697      
698      var localList = self.localList(pathname);
699
700      // loop remote files
701      loopRemoteFiles(localList, remoteList, function (err) {
702        if (err) { return handleRemoteListDone(err); }
703        // loop local files
704        loopLocalFiles(localList, remoteList, function (err) {
705          if (err) { return handleRemoteListDone(err); }
706          // handle modified files
707          handleModifiedFiles(localList, remoteList, handleRemoteListDone);          
708        });
709      });
710    } // handleRemoteList
711
712
713    function getRemoteDirStatsList (getRemoteDirStatsListCallback) {
714    
715      self.remoteList(pathname, function (err, remoteList) {
716        if (err) { return getRemoteDirStatsListCallback(err); }
717        handleRemoteList(remoteList, function (err) {
718          var dirFiles, localDirNode;
719          if (err) {
720            getRemoteDirStatsListCallback(err, pathname);
721          }
722          else {
723            localDirNode = self.localFs.getNode(pathname);
724            localDirNode.remoteStats.fetched = Date.now();
725            if (recursive) {
726              // sync recursively
727              dirFiles = self.localList(pathname);
728              FSRSYNC.eachAsync(
729                Object.keys(dirFiles),
730                function (filename, dirfileDone) {
731                  var stats = dirFiles[filename];
732                  if (stats && stats.isDirectory()) {
733                    self.syncDir(pathname + filename, {recursive: true}, dirfileDone);
734                  }
735                  else {
736                    dirfileDone();
737                  }
738                },
739                function (err) {
740                  // console.log('dir files done for ' + pathname);
741                  getRemoteDirStatsListCallback(err, pathname);
742                }
743              );
744            }
745            else {
746              getRemoteDirStatsListCallback(null, pathname);
747            }
748          }
749        });
750      });
751    } // get remote dir stats list
752
753
754    this.remoteStat(pathname, function (err, remoteStats) {
755
756      var localDirNode;
757
758      if (!err && remoteStats) {
759        // dir exists on remote
760        if (existsOnLocal) {
761          // exists both on local and remote fs
762          localDirNode = self.localFs.getNode(pathname);
763          localDirNode.remoteStats = remoteStats;
764          getRemoteDirStatsList(callback);
765        }
766        else {
767          // create on local fs
768          self.createLocal(
769            pathname,
770            remoteStats,
771            function (err) {
772              if (err) {
773                return callback(err);
774              }
775              getRemoteDirStatsList(callback);
776            }
777          );
778        }
779      }
780      else {
781        // dir not on remote
782        if (existsOnLocal) {
783          // create on remote
784          self.createRemote(
785            pathname,
786            self.localFs.statSync(pathname),
787            function (err) {
788              if (err) {
789                return callback(err);
790              }
791              self.syncDir(pathname, options, callback);
792            }
793          );
794        }
795        else {
796          // does not exist anywhere
797          callback(null);
798        }
799      }
800    });
801  };  // FSRSYNC.syncDir
802
803
804  FSRSYNC.prototype.syncExistingFiles = function (filename, localStats, remoteStats, callback) {
805
806    var self = this,
807      localFileNode,
808      remoteFileHasChanged,
809      localFileHasChanged;
810
811    // file exists on local and remote fs
812    localFileNode = self.localFs.getNode(filename);
813
814    remoteFileHasChanged = localFileNode.remoteStats ? 
815      localFileNode.remoteStats.mtime !== remoteStats.mtime : true;
816    
817    localFileHasChanged = localFileNode.remoteStats ?
818      localFileNode.mtime !== localFileNode.remoteStats.mtime : true;
819
820    if (remoteFileHasChanged && localFileHasChanged) {
821      // file changed both on remote and local fs
822      callback(new Error('ECONFLICT'), filename);
823    }
824    else if (remoteFileHasChanged) {
825      // file only has changed on remote fs: update local file
826      self.remoteReadFile(
827        filename, 
828        function (err, data) {
829          
830          if (err) {
831            return callback(null, filename);
832          }
833          
834          self.localFs.writeFile(filename, data, function (err) {
835
836            if (err) { return callback(err); }      
837
838            localFileNode.remoteStats = remoteStats;
839            localFileNode.remoteStats.fetched = Date.now();
840
841            // update local stats
842            localFileNode.ctime = remoteStats.birthtime;
843            localFileNode.mtime = remoteStats.mtime;
844            localFileNode.atime = remoteStats.atime;
845
846            callback(null, filename);
847          });
848        }
849      );
850    }
851    else if (localFileHasChanged) {
852      // file only has changed on local fs: update remote file
853      self.localFs.readFile(filename, function (err, data) {
854        if (err) { return callback(err,filename); }
855        self.remoteWriteFile(
856          filename, 
857          data, 
858          function (err, remoteStats) {
859            if (err) { return callback(err, filename); }
860            localFileNode.remoteStats = remoteStats;
861            localFileNode.remoteStats.fetched = Date.now();
862            localFileNode.mtime = remoteStats.mtime;
863            callback(null, filename);
864          }
865        );
866      });
867    }
868    else {
869      callback(null, filename);
870    }    
871  };  // syncExisting
872
873
874  FSRSYNC.prototype.createRemote = function (filename, localStats, callback) {
875
876    var self = this;
877
878    if (localStats.isDirectory()) {
879      // create the directory on remote
880      this.createRemoteDir(filename, callback);
881    }
882    else {
883      // create file on remote 
884      this.createRemoteFile(
885        filename,
886        self.localFs.readFileSync(filename), 
887        callback
888      );
889    }
890  };  // createRemote
891
892
893  FSRSYNC.prototype.createRemoteDir = function (pathname, callback) {
894
895    var self = this,
896      fs = self.localFs;
897
898    this.ensureRemotePath(fs.dirname(pathname), function (err) {
899
900      if (err) {
901        callback(err);
902      }
903      else {      
904        self.remoteMkdir( 
905          pathname,
906          function (err, remoteStats) {
907
908            var localFileNode;        
909            
910            if (err) { return callback(err); }
911
912            localFileNode = fs.getNode(pathname);
913            localFileNode.remoteStats = remoteStats;
914            localFileNode.remoteStats.fetched = Date.now();
915            localFileNode.mtime = remoteStats.mtime;
916
917            callback(null);
918          }
919        );
920      }
921    });
922  };  // createRemoteDir
923
924
925  FSRSYNC.prototype.createRemoteFile = function (filename, localContent, callback) {
926
927    var self = this,
928      fs = self.localFs;
929
930    this.ensureRemotePath(fs.dirname(filename), function (err) {
931      
932      if (err) {
933        callback(err);
934      }
935      else {    
936        self.remoteWriteFile(
937          filename, 
938          localContent,
939          function (err, remoteStats) {
940
941            var localFileNode;        
942            
943            if (err) { return callback(err); }
944
945            localFileNode = self.localFs.getNode(filename);
946            localFileNode.remoteStats = remoteStats;
947            localFileNode.remoteStats.fetched = Date.now();
948            localFileNode.mtime = remoteStats.mtime;
949
950            callback(null);
951          }
952        );
953        }
954      });
955  };  // createLocalFile
956
957
958  FSRSYNC.prototype.ensureRemotePath = function (pathname, callback) {
959
960    var self = this;
961
962    if ('/' === pathname) {
963      callback();
964    }
965    else {
966      this.remoteStat(pathname, function (err) {
967        if (err) {
968          self.remoteMkdirp( 
969            pathname,
970            callback
971          );
972        }
973        else {
974          callback();
975        }
976      });
977    }
978  };  // ensureRemotePath
979
980
981  FSRSYNC.prototype.createLocal = function (filename, remoteStats, callback) {
982
983    var self = this;
984
985    if (remoteStats.isDirectory) {
986      // create the directory locally
987      this.createLocalDir(
988        filename,
989        remoteStats, 
990        callback
991      );
992    }
993    else {
994      // create file locally
995      this.remoteReadFile(
996        filename, 
997        function (err, data) {
998          
999          if (err) {
1000            return callback(err);
1001          }
1002          
1003          self.createLocalFile(
1004            filename,
1005            data,
1006            remoteStats, 
1007            callback
1008          );
1009        }
1010      );
1011    }
1012  };  // createLocal
1013
1014
1015  FSRSYNC.prototype.ensureLocalPath = function (pathname) {
1016    var localFs = this.localFs;
1017    if (!localFs.existsSync(pathname)) {
1018      localFs.mkdirpSync(pathname);
1019    }
1020  };  // ensureLocalPath
1021
1022
1023  FSRSYNC.prototype.createLocalDir = function (pathname, remoteStats, callback ) {
1024    
1025    var self = this;
1026
1027    this.ensureLocalPath(this.localFs.dirname(pathname));
1028
1029    this.localFs.mkdir(pathname, function (err) {
1030      
1031      var time = Date.now(),
1032        parentDirNode,
1033        localDirNode;
1034
1035      if (err) { return callback(err); }
1036
1037      localDirNode = self.localFs.getNode(pathname);
1038      localDirNode.remoteStats = remoteStats;
1039      localDirNode.remoteStats.fetched = false;
1040
1041      // update local stats
1042      localDirNode.ctime = remoteStats.birthtime;
1043      localDirNode.mtime = remoteStats.mtime;
1044      localDirNode.atime = remoteStats.atime;
1045
1046      parentDirNode = self.localFs.getNode(self.localFs.dirname(pathname));
1047
1048      parentDirNode.atime = time;
1049      parentDirNode.mtime = time;
1050
1051      callback();
1052    });
1053  };  // createLocalDir
1054
1055
1056  FSRSYNC.prototype.createLocalFile = function (filename, remoteContent, remoteStats, callback) {
1057
1058    var self = this;
1059
1060    // console.log(filename, remoteStats);
1061    this.ensureLocalPath(this.localFs.dirname(filename));
1062
1063    this.localFs.writeFile(filename, remoteContent, function (err) {
1064
1065      var time = Date.now(),
1066        parentDirNode,
1067        localFileNode;
1068
1069      if (err) { return callback(err); }
1070      
1071      localFileNode = self.localFs.getNode(filename);
1072
1073      localFileNode.remoteStats = remoteStats;
1074      localFileNode.remoteStats.fetched = time;
1075
1076      // update local stats
1077      localFileNode.ctime = remoteStats.birthtime;
1078      localFileNode.mtime = remoteStats.mtime;
1079      localFileNode.atime = remoteStats.atime;
1080
1081      parentDirNode = self.localFs.getNode(self.localFs.dirname(filename));
1082
1083      parentDirNode.atime = time;
1084      parentDirNode.mtime = time;
1085
1086      callback(null);
1087    });
1088  };  // createLocalFile
1089
1090
1091  return FSRSYNC;
1092
1093}));
1094
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Appium Xcuitest Driver on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)