var nock = require('nock'),
    async = require('async');
require('../helpers/setup');

describe("mjson tests", function() {

  var server;

  before(function() {
    server = nock('http://localhost:5555');
  });

  describe("promise api", function() {
    var browser;

    before(function(done) {
      server.post('/session').reply(303, "OK", {
        'Location': '/session/1234'
      }).get('/session/1234').reply(200, {
          status: 0,
          sessionId: '1234',
          value: {}
      });

      browser = wd.promiseChainRemote('http://localhost:5555/');
      browser
        .init()
        .nodeify(done);
    });

    describe("by ios uiautomation", function() {

      it("element methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/element', {"using":"-ios uiautomation","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          });
        server
          .post('/session/1234/elements', {"using":"-ios uiautomation","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .element('-ios uiautomation', 'random stuff')
            .should.eventually.exist
          .elementByIosUIAutomation('random stuff')
            .should.eventually.exist
          .elementByIosUIAutomationOrNull('random stuff')
            .should.eventually.exist
          .elementByIosUIAutomationIfExists('random stuff')
            .should.eventually.exist
          .hasElementByIosUIAutomation('random stuff')
            .should.eventually.be.ok
          .nodeify(done);
      });

      it("elements methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"-ios uiautomation","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .elements('-ios uiautomation', 'random stuff')
            .should.eventually.exist
          .elementsByIosUIAutomation('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

      it("wait methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"-ios uiautomation","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .waitForElement('-ios uiautomation', 'random stuff')
            .should.eventually.exist
          .waitForElementByIosUIAutomation('random stuff')
            .should.eventually.exist
          .waitForElementsByIosUIAutomation('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

    });


    describe("by ios class chain", function() {

      it("element methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/element', {"using":"-ios class chain","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          });
        server
          .post('/session/1234/elements', {"using":"-ios class chain","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .element('-ios class chain', 'random stuff')
            .should.eventually.exist
          .elementByIosClassChain('random stuff')
            .should.eventually.exist
          .elementByIosClassChainOrNull('random stuff')
            .should.eventually.exist
          .elementByIosClassChainIfExists('random stuff')
            .should.eventually.exist
          .hasElementByIosClassChain('random stuff')
            .should.eventually.be.ok
          .nodeify(done);
      });

      it("elements methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"-ios class chain","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .elements('-ios class chain', 'random stuff')
            .should.eventually.exist
          .elementsByIosClassChain('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

      it("wait methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"-ios class chain","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .waitForElement('-ios class chain', 'random stuff')
            .should.eventually.exist
          .waitForElementByIosClassChain('random stuff')
            .should.eventually.exist
          .waitForElementsByIosClassChain('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

    });

    describe("by android uiautomator", function() {

      it("element methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/element', {"using":"-android uiautomator","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          });
        server
          .post('/session/1234/elements', {"using":"-android uiautomator","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .element('-android uiautomator', 'random stuff')
            .should.eventually.exist
          .elementByAndroidUIAutomator('random stuff')
            .should.eventually.exist
          .elementByAndroidUIAutomatorOrNull('random stuff')
            .should.eventually.exist
          .elementByAndroidUIAutomatorIfExists('random stuff')
            .should.eventually.exist
          .hasElementByAndroidUIAutomator('random stuff')
            .should.eventually.be.ok
          .nodeify(done);
      });

      it("elements methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"-android uiautomator","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .elements('-android uiautomator', 'random stuff')
            .should.eventually.exist
          .elementsByAndroidUIAutomator('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

      it("wait methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"-android uiautomator","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .waitForElement('-android uiautomator', 'random stuff')
            .should.eventually.exist
          .waitForElementByAndroidUIAutomator('random stuff')
            .should.eventually.exist
          .waitForElementsByAndroidUIAutomator('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

    });

    describe("by accessibility id", function() {

      it("element methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/element', {"using":"accessibility id","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          });
        server
          .post('/session/1234/elements', {"using":"accessibility id","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .element('accessibility id', 'random stuff')
            .should.eventually.exist
          .elementByAccessibilityId('random stuff')
            .should.eventually.exist
          .elementByAccessibilityIdOrNull('random stuff')
            .should.eventually.exist
          .elementByAccessibilityIdIfExists('random stuff')
            .should.eventually.exist
          .hasElementByAccessibilityId('random stuff')
            .should.eventually.be.ok
          .nodeify(done);
      });

      it("elements methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"accessibility id","value":"random stuff"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .elements('accessibility id', 'random stuff')
            .should.eventually.exist
          .elementsByAccessibilityId('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

      it("wait methods should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/elements', {"using":"accessibility id","value":"random stuff"})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: [{ELEMENT: '0'}],
          });
        browser
          .waitForElement('accessibility id', 'random stuff')
            .should.eventually.exist
          .waitForElementByAccessibilityId('random stuff')
            .should.eventually.exist
          .waitForElementsByAccessibilityId('random stuff')
            .should.eventually.exist
          .nodeify(done);
      });

    });

    describe("actions", function() {

      it("touch actions should work", function(done) {
        browser.chain()
        .then(function() {
          nock.cleanAll();
          server
            .post('/session/1234/element', {"using":"id","value":"random"})
            .reply(200, {
              status: 0,
              sessionId: '1234',
              value: {ELEMENT: '0'},
            })
            .post('/session/1234/touch/perform', {"actions": [
              {"action":"press","options":{x: 100, y: 5}},
              {"action":"release","options":{}}
            ]})
            .times(2)
            .reply(200, {
              status: 0,
              sessionId: '1234',
              // TODO check what the return is like
              value: [{'not sure': '0'}],
            });
          var el;
          return browser
            .elementById('random').then(function(_el) { el=_el; })
            .then(function() {
              var action = new wd.TouchAction();
              action.press({x: 100, y: 5}).release();
              return browser
                .performTouchAction(action);
            }).then(function() {
              var action = new wd.TouchAction(browser);
              action.press({x: 100, y: 5}).release();
              return action.perform();
            });
        }).nodeify(done);
      });

      it("multi actions should work", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/element', {"using":"id","value":"random"})
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          });
        var el;
        browser
          .elementById('random').then(function(_el) { el = _el; })
          .then(function() {
            nock.cleanAll();
            server
              .post('/session/1234/touch/multi/perform', {
                "elementId":"0",
                "actions":[
                  [{"action":"tap","options":{"x": 100,"y":200}}],
                  [{"action":"tap","options":{"x":50,"y":25}}]
                ]})
              .times(4)
              .reply(200, {
                status: 0,
                sessionId: '1234',
                // TODO check what the return is like
                value: [{'not sure': '0'}],
              });
          })
          .then(function() {
            var a1 = new wd.TouchAction().tap({x: 100, y: 200});
            var a2 = new wd.TouchAction().tap({x: 50, y: 25});
            var ma = new wd.MultiAction().add(a1, a2);
            return browser.performMultiAction(el, ma);
          })
          .then(function() {
            var a1 = new wd.TouchAction().tap({x: 100, y: 200});
            var a2 = new wd.TouchAction().tap({x: 50, y: 25});
            var ma = new wd.MultiAction().add(a1, a2);
            return browser.performMultiAction(el, ma);
          })
          .then(function() {
            var a1 = new wd.TouchAction().tap({x: 100, y: 200});
            var a2 = new wd.TouchAction().tap({x: 50, y: 25});
            var ma = new wd.MultiAction().add(a1, a2);
            return el.performMultiAction(ma);
          })
          .then(function() {
            var a1 = new wd.TouchAction().tap({x: 100, y: 200});
            var a2 = new wd.TouchAction().tap({x: 50, y: 25});
            var ma = new wd.MultiAction(el).add(a1, a2);
            return ma.perform();
          })
          .then(function() {
            nock.cleanAll();
            server
              .post('/session/1234/touch/multi/perform', {
                "actions":[
                  [{"action":"tap","options":{"x": 100,"y":200}}],
                  [{"action":"tap","options":{"x":50,"y":25}}]
                ]})
              .times(2)
              .reply(200, {
                status: 0,
                sessionId: '1234',
                // TODO check what the return is like
                value: [{'not sure': '0'}],
              });
          })
          .then(function() {
            var a1 = new wd.TouchAction().tap({x: 100, y: 200});
            var a2 = new wd.TouchAction().tap({x: 50, y: 25});
            var ma = new wd.MultiAction().add(a1, a2);
            return browser.performMultiAction(ma);
          })
          .then(function() {
            var a1 = new wd.TouchAction().tap({x: 100, y: 200});
            var a2 = new wd.TouchAction().tap({x: 50, y: 25});
            var ma = new wd.MultiAction(browser).add(a1, a2);
            return ma.perform();
          })
          .nodeify(done);
      });
    });

    describe("device methods", function() {

      it("shakeDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/shake', {})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        browser
          .shakeDevice()
          .shake()
          .nodeify(done);
      });

      it("touchId", function(done){
        nock.cleanAll();
        server
          .post('/session/1234/appium/simulator/touch_id', {match: true})
          .times(1)
          .reply(200, {
            sessionId: '1234',
          });

        browser
          .touchId(true)
          .nodeify(done);
      });

      it("toggleTouchIdEnrollment", function(done){
        nock.cleanAll();
        server
          .post('/session/1234/appium/simulator/toggle_touch_id_enrollment')
          .times(1)
          .reply(200, {
            sessionId: '1234',
          });

        browser
          .toggleTouchIdEnrollment(true)
          .nodeify(done);
      });

      it("lockDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/lock', {seconds: 3})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        browser
          .lockDevice(3)
          .lock(3)
          .nodeify(done);
      });

      describe("deviceKeyEvent", function() {
        it("keycode only", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/appium/device/keyevent', {keycode: 3})
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .deviceKeyEvent(3)
            .nodeify(done);
        });
        it("keycode only + metastate", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/appium/device/keyevent', {keycode: 3, metastate: "abcd"})
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .deviceKeyEvent(3, "abcd")
            .nodeify(done);
        });
        it("pressDeviceKey", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/appium/device/keyevent', {keycode: 3})
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .pressDeviceKey(3)
            .nodeify(done);
        });
      });

      describe("pressKeycode", function() {
        it("keycode only", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/appium/device/press_keycode', {keycode: 3})
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .pressKeycode(3)
            .nodeify(done);
        });
        it("keycode + metastate", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/appium/device/press_keycode', {keycode: 3, metastate: "abcd"})
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .pressKeycode(3, "abcd")
            .nodeify(done);
        });
      });

      describe("rotateDevice", function() {
        it("without element", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/appium/device/rotate',
              {x: 114, y: 198, duration: 5, radius: 3, rotation: 220, touchCount: 2})
            .times(2)
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .rotateDevice({x: 114, y: 198, duration: 5, radius: 3, rotation: 220, touchCount: 2})
            .rotate({x: 114, y: 198, duration: 5, radius: 3, rotation: 220, touchCount: 2})
            .nodeify(done);
        });
        it("with element", function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/element', {"using":"id","value":"random"})
            .reply(200, {
              status: 0,
              sessionId: '1234',
              value: {ELEMENT: '0'},
            })
            .post('/session/1234/appium/device/rotate',
              {x: 114, y: 198, duration: 5, radius: 3, rotation: 220, touchCount: 2, element: "0"})
            .times(3)
            .reply(200, {
              status: 0,
              sessionId: '1234',
            });
          browser
            .elementById('random').then(function(el) {
              return browser
                .rotateDevice(el, {x: 114, y: 198, duration: 5, radius: 3,
                  rotation: 220, touchCount: 2})
                .rotate(el, {x: 114, y: 198, duration: 5, radius: 3,
                  rotation: 220, touchCount: 2})
                .then(function() {
                  return el
                    .rotate({x: 114, y: 198, duration: 5, radius: 3,
                      rotation: 220, touchCount: 2});
                });
            })
            .nodeify(done);
        });
      });

      it("getCurrentDeviceActivity", function(done) {
        nock.cleanAll();
        server
          .get('/session/1234/appium/device/current_activity')
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: '.activities.PeopleActivity'
          });
        browser
          .getCurrentDeviceActivity()
          .should.become('.activities.PeopleActivity')
          .getCurrentActivity()
          .should.become('.activities.PeopleActivity')
          .nodeify(done);
      });

      it("getCurrentPackage", function(done) {
        nock.cleanAll();
        server
          .get('/session/1234/appium/device/current_package')
          .times(1)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: 'org.fake.package'
          });
        browser
          .getCurrentPackage()
          .should.become('org.fake.package')
          .nodeify(done);
      });

      it("installAppOnDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/install_app', {appPath: "http://appium.s3.amazonaws.com/UICatalog6.0.app.zip"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        browser
          .installAppOnDevice("http://appium.s3.amazonaws.com/UICatalog6.0.app.zip")
          .installApp("http://appium.s3.amazonaws.com/UICatalog6.0.app.zip")
          .nodeify(done);
      });

      it("removeAppFromDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/remove_app', {appId: "rubish"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        browser
          .removeAppFromDevice("rubish")
          .removeApp("rubish")
          .nodeify(done);
      });

      it("isAppInstalledOnDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/app_installed', {bundleId: "coolApp"})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: true // TODO check return
          });
        browser
          .isAppInstalledOnDevice("coolApp")
            .should.eventually.be.ok
          .isAppInstalled("coolApp")
            .should.eventually.be.ok
          .nodeify(done);
      });

      it("hideDeviceKeyboard, passing key", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/hide_keyboard', {keyName: "Done"})
          .times(1)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        server
          .post('/session/1234/appium/device/hide_keyboard')
          .times(1)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        server
          .post('/session/1234/appium/device/hide_keyboard', {strategy: 'tapOutside'})
          .times(1)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        server
          .post('/session/1234/appium/device/hide_keyboard', {strategy: 'pressKey', key:'Done'})
          .times(1)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        browser
          .hideKeyboard()
          .hideDeviceKeyboard("Done")
          .hideDeviceKeyboard({strategy: 'tapOutside'})
          .hideDeviceKeyboard({strategy: 'pressKey', key:'Done'})
          .nodeify(done);
      });

      it("pushFileToDevice", function(done) {
        var remotePath = '/data/local/tmp/remote.txt';
        var stringData = "random string data " + Math.random();
        var base64Data = new Buffer(stringData).toString('base64');
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/push_file', {path: remotePath, data: base64Data})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
          });
        browser
          .pushFileToDevice(remotePath, base64Data)
          .pushFile(remotePath, base64Data)
          .nodeify(done);
      });

      it("pullFileFromDevice", function(done) {
        var remotePath = '/data/local/tmp/remote.txt';
        var stringData = "random string data " + Math.random();
        var base64Data = new Buffer(stringData).toString('base64');
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/pull_file', {path: remotePath})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: base64Data // TODO: check function return
          });
        browser
          .pullFileFromDevice(remotePath, base64Data)
            .should.become(base64Data)
          .pullFile(remotePath, base64Data)
            .should.become(base64Data)
          .nodeify(done);
      });

      it("pullFolderFromDevice", function(done) {
        var remotePath = '/data/local/tmp/remote';
        var stringData = "not a zip but that doesn't matter " + Math.random();
        var base64Data = new Buffer(stringData).toString('base64');
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/pull_folder', {path: remotePath})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: base64Data // TODO: check function return
          });
        browser
          .pullFolderFromDevice(remotePath, base64Data)
            .should.become(base64Data)
          .pullFolder(remotePath, base64Data)
            .should.become(base64Data)
          .nodeify(done);
      });

      it("toggleAirplaneModeOnDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/toggle_airplane_mode', {})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .toggleAirplaneModeOnDevice()
          .toggleAirplaneMode()
          .toggleFlightMode()
          .nodeify(done);
      });

      it("toggleWiFiOnDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/toggle_wifi', {})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .toggleWiFiOnDevice()
          .toggleWiFi()
          .nodeify(done);
      });

      it("toggleLocationServicesOnDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/toggle_location_services', {})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .toggleLocationServicesOnDevice()
          .toggleLocationServices()
          .nodeify(done);
      });

      it("toggleDataOnDevice", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/toggle_data', {})
          .times(2)
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .toggleDataOnDevice()
          .toggleData()
          .nodeify(done);
      });

      it("launchApp", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/app/launch', {})
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .launchApp()
          .nodeify(done);
      });

      it("closeApp", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/app/close', {})
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .closeApp()
          .nodeify(done);
      });

      it("resetApp", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/app/reset', {})
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .resetApp()
          .nodeify(done);
      });

      it("backgroundApp", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/app/background', {seconds: 3})
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .backgroundApp(3)
          .nodeify(done);
      });

      it("endTestCoverageForApp", function(done) {
        var intent = "android.intent.action.BOOT_COMPLETED";
        var path = "/random/path";
        var stringData = "random string data " + Math.random();
        var base64Data = new Buffer(stringData).toString('base64');
        nock.cleanAll();
        server
          .post('/session/1234/appium/app/end_test_coverage', {intent: intent, path: path})
          .times(3)
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: base64Data
          });
        browser
          .endTestCoverageForApp(intent, path)
          .endTestCoverage(intent, path)
          .endCoverage(intent, path)
          .should.become(base64Data)
          .nodeify(done);
      });

      describe("complexFindInApp", function() {
        it("one element", function(done) {
          var selector = "abcd";
          nock.cleanAll();
          server
            .post('/session/1234/appium/app/complex_find', {selector: "abcd"})
            .times(2)
            .reply(200, {
              status: 0,
              sessionId: '1234',
              value: {ELEMENT: '0'}
            });
          browser
            .complexFindInApp(selector)
              .then(function(el) {
                el.value.should.equal('0');
              })
            .complexFind(selector)
              .then(function(el) {
                el.value.should.equal('0');
              }).nodeify(done);
        });
        it("element array", function(done) {
          var selector = "all";
          nock.cleanAll();
          server
            .post('/session/1234/appium/app/complex_find', {selector: "all"})
            .times(2)
            .reply(200, {
              status: 0,
              sessionId: '1234',
              value: [{ELEMENT: '0'}]
            });
          browser
            .complexFindInApp(selector)
              .then(function(els) {
                els[0].value.should.equal('0');
              })
            .complexFind(selector)
              .then(function(els) {
                els[0].value.should.equal('0');
              }).nodeify(done);
        });
      });

      it("getAppStrings", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/app/strings', {language: 'en'})
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: 'abcdefghj'
          });
        browser
          .getAppStrings('en')
          .nodeify(done);
      });

      it("setImmediateValueInApp", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/element', {"using":"id","value":"random"})
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          })
          .post('/session/1234/appium/element/0/value', {value: "12345"})
          .times(4)
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .elementById("random")
          .then(function(el) {
            return browser
              .setImmediateValueInApp(el, "12345")
              .setImmediateValue(el, "12345")
              .then(function() {
                return el
                  .setImmediateValueInApp("12345")
                  .setImmediateValue("12345");
              });
          })
          .nodeify(done);
      });

      it("setNetworkConnection", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/network_connection', {parameters: {type: 5}})
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: 5
          });
        browser
          .setNetworkConnection(5)
            .should.eventually.equal(5)
          .nodeify(done);
      });

      it("getNetworkConnection", function(done) {
        nock.cleanAll();
        server
          .get('/session/1234/network_connection')
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {value: 5}
          });
        browser
          .getNetworkConnection()
            .should.eventually.deep.equal({value: 5})
          .nodeify(done);
      });

      it("openNotifications", function(done) {
        nock.cleanAll();
        server
          .post('/session/1234/appium/device/open_notifications')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .openNotifications()
          .nodeify(done);
      });

      it("availableIMEEngines", function (done) {
        nock.cleanAll();
        server
          .get('/session/1234/ime/available_engines')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .availableIMEEngines()
          .nodeify(done);
      });

      it("activateIMEEngine", function (done) {
        nock.cleanAll();
        server
          .post('/session/1234/ime/activate')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .activateIMEEngine('some.ime')
          .nodeify(done);
      });

      it("deactivateIMEEngine", function (done) {
        nock.cleanAll();
        server
          .post('/session/1234/ime/deactivate')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .deactivateIMEEngine()
          .nodeify(done);
      });

      it("isIMEActive", function (done) {
        nock.cleanAll();
        server
          .get('/session/1234/ime/activated')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .isIMEActive()
          .nodeify(done);
      });

      it("activeIMEEngine", function (done) {
        nock.cleanAll();
        server
          .get('/session/1234/ime/active_engine')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .activeIMEEngine()
          .nodeify(done);
      });

      it("getDeviceTime", function (done) {
        nock.cleanAll();
        server
          .get('/session/1234/appium/device/system_time')
          .reply(200, {
            status: 0,
            sessionId: '1234'
          });
        browser
          .getDeviceTime()
          .nodeify(done);
      });
    });
  });

  describe("async callback api", function() {
    var browser;
    before(function(done) {
      server.post('/session').reply(303, "OK", {
        'Location': '/session/1234'
      }).get('/session/1234').reply(200, {
          status: 0,
          sessionId: '1234',
          value: {}
      });
      browser = wd.remote('http://localhost:5555/');
      browser.init(done);
    });

    it("touch actions should work", function(done) {
      nock.cleanAll();
      server
        .post('/session/1234/element', {"using":"id","value":"random"})
        .reply(200, {
          status: 0,
          sessionId: '1234',
          value: {ELEMENT: '0'},
        })
        .post('/session/1234/touch/perform', {"actions": [{"action":"tap","options":{}}]})
        .times(2)
        .reply(200, {
          status: 0,
          sessionId: '1234',
          // TODO check what the return is like
          value: [{'not sure': '0'}],
        });
      var el;
      async.series([
        function(done) {
          browser.elementById('random', function(err, _el) {
            should.not.exist(err);
            el = _el;
            done();
          });
        },
        function(done) {
          var action = new wd.TouchAction().tap();
          browser.performTouchAction(action, function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
        function(done) {
          var action = new wd.TouchAction(browser).tap();
          action.perform(function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
      ], done);
    });

    it("multi actions should work", function(done) {
        server
          .post('/session/1234/element', {"using":"id","value":"random"})
          .reply(200, {
            status: 0,
            sessionId: '1234',
            value: {ELEMENT: '0'},
          });
      var el;
      async.series([
        function(done) {
          browser.elementById('random', function(err, _el) {
            should.not.exist(err);
            el = _el;
            done();
          });
        },
        function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/touch/multi/perform', {
              "elementId":"0",
              "actions":[
                [{"action":"tap","options":{x: 100, y: 200}}],
                [{"action":"tap","options":{x: 50, y: 25}}]
              ]})
            .times(3)
            .reply(200, {
              status: 0,
              sessionId: '1234',
              // TODO check what the return is like
              value: [{'not sure': '0'}],
            });
          done();
        },
        function(done) {
          var a1 = new wd.TouchAction().tap({x: 100, y: 200});
          var a2 = new wd.TouchAction().tap({x: 50, y: 25});
          var ma = new wd.MultiAction().add(a1, a2);
          browser.performMultiAction(el, ma, function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
        function(done) {
          var a1 = new wd.TouchAction().tap({x: 100, y: 200});
          var a2 = new wd.TouchAction().tap({x: 50, y: 25});
          var ma = new wd.MultiAction().add(a1, a2);
          el.performMultiAction(ma, function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
        function(done) {
          var a1 = new wd.TouchAction().tap({x: 100, y: 200});
          var a2 = new wd.TouchAction().tap({x: 50, y: 25});
          var ma = new wd.MultiAction(el).add(a1, a2);
          ma.perform(function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
        function(done) {
          nock.cleanAll();
          server
            .post('/session/1234/touch/multi/perform', {
              "actions":[
                [{"action":"tap","options":{x: 100, y: 200}}],
                [{"action":"tap","options":{x: 50, y: 25}}]
              ]})
            .times(2)
            .reply(200, {
              status: 0,
              sessionId: '1234',
              // TODO check what the return is like
              value: [{'not sure': '0'}],
            });
          done();
        },
        function(done) {
          var a1 = new wd.TouchAction().tap({x: 100, y: 200});
          var a2 = new wd.TouchAction().tap({x: 50, y: 25});
          var ma = new wd.MultiAction().add(a1, a2);
          browser.performMultiAction(ma, function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
        function(done) {
          var a1 = new wd.TouchAction().tap({x: 100, y: 200});
          var a2 = new wd.TouchAction().tap({x: 50, y: 25});
          var ma = new wd.MultiAction(browser).add(a1, a2);
          ma.perform(function(err, res) {
            should.not.exist(err);
            res.should.exist;
            done();
          });
        },
      ], done);

    });

  });
});
