Repository: Codeception/CodeceptJS
Pull request #915 (2018-02-06 10:38:55)

Puppeteer helper, proceedClick should return _waitForAction
Since [click](https://github.com/Codeception/CodeceptJS/blob/master/lib/helper/Puppeteer.js#L625-L641) return the value from `proceedClick`.
I think `proceedClick` should return waitForAction just like other actions.

DavertMik: In this case, this should not be a difference. 
The idea is the same: wait for action after the action is performed,

Patch:
diff --git a/docs/webapi/grabNumberOfOpenTabs.mustache b/docs/webapi/grabNumberOfOpenTabs.mustache
new file mode 100644
index 00000000..faccd799
--- /dev/null
+++ b/docs/webapi/grabNumberOfOpenTabs.mustache
@@ -0,0 +1,5 @@
+Grab number of open tabs
+
+```js
+I.grabNumberOfOpenTabs();
+```
\ No newline at end of file
diff --git a/lib/data/context.js b/lib/data/context.js
index 7f042ca0..c40c7981 100644
--- a/lib/data/context.js
+++ b/lib/data/context.js
@@ -46,6 +46,11 @@ module.exports = function (context) {
   };
 };
 
+function isTableDataRow(row) {
+  const has = Object.prototype.hasOwnProperty;
+  return has.call(row, 'data') && has.call(row, 'skip');
+}
+
 function detectDataType(dataTable) {
   if (dataTable instanceof DataTable) {
     return dataTable.rows;
@@ -64,7 +69,15 @@ function detectDataType(dataTable) {
     return dataTable();
   }
   if (Array.isArray(dataTable)) {
-    return dataTable;
+    return dataTable.map((item) => {
+      if (isTableDataRow(item)) {
+        return item;
+      }
+      return {
+        data: item,
+        skip: false,
+      };
+    });
   }
 
   throw new Error('Invalid data type. Data accepts either: DataTable || generator || Array || function');
diff --git a/lib/helper/Nightmare.js b/lib/helper/Nightmare.js
index 77b73912..a0f13478 100644
--- a/lib/helper/Nightmare.js
+++ b/lib/helper/Nightmare.js
@@ -480,6 +480,19 @@ class Nightmare extends Helper {
     stringIncludes('HTML source of a page').negate(text, source);
   }
 
+  /**
+   * asserts that an element appears a given number of times in the DOM
+   * Element is located by label or name or CSS or XPath.
+   *
+   * ```js
+   * I.seeNumberOfElements('#submitBtn', 1);
+   * ```
+   */
+  async seeNumberOfElements(selector, num) {
+    const elements = await this._locate(selector);
+    return equals(`expected number of elements (${selector}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
+  }
+
 
   /**
    * {{> ../webapi/click }}
diff --git a/lib/helper/Protractor.js b/lib/helper/Protractor.js
index ffa5eb05..08b0a82c 100644
--- a/lib/helper/Protractor.js
+++ b/lib/helper/Protractor.js
@@ -874,6 +874,14 @@ class Protractor extends Helper {
     return client.switchTo().window(newHandle);
   }
 
+  /**
+   * {{> ../webapi/grabNumberOfOpenTabs }}
+   */
+  async grabNumberOfOpenTabs() {
+    const pages = await this.browser.getAllWindowHandles();
+    return pages.length;
+  }
+
   /**
    * {{> ../webapi/wait }}
    */
diff --git a/lib/helper/Puppeteer.js b/lib/helper/Puppeteer.js
index 6866c7ec..00cd507a 100644
--- a/lib/helper/Puppeteer.js
+++ b/lib/helper/Puppeteer.js
@@ -35,13 +35,18 @@ const consoleLogStore = new Console();
  *
  * This helper should be configured in codecept.json
  *
- * * `url` - base url of website to be tested
- * * `show` (optional, default: false) - show Google Chrome window for debug.
- * * `disableScreenshots` (optional, default: false)  - don't save screenshot on failure.
- * * `uniqueScreenshotNames` (optional, default: false)  - option to prevent screenshot override if you have scenarios with the same name in different suites.
+ * * `url`: base url of website to be tested
+ * * `show`: (optional, default: false) - show Google Chrome window for debug.
+ * * `restart`: (optional, default: true) - restart browser between tests.
+ * * `disableScreenshots`: (optional, default: false)  - don't save screenshot on failure.
+ * * `uniqueScreenshotNames`: (optional, default: false)  - option to prevent screenshot override if you have scenarios with the same name in different suites.
+ * * `keepBrowserState`: (optional, default: false) - keep browser state between tests when `restart` is set to false.
+ * * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to false.
  * * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
  * * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
  * * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
+ * * `userAgent`: (optional) user-agent string.
+ * * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
  * * `chrome`: (optional) pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions). Example
  *
  * ```js
@@ -61,7 +66,10 @@ class Puppeteer extends Helper {
       fullPageScreenshots: true,
       disableScreenshots: false,
       uniqueScreenshotNames: false,
+      manualStart: false,
       restart: true,
+      keepCookies: false,
+      keepBrowserState: false,
       show: false,
       defaultPopupAction: 'accept',
     };
@@ -100,11 +108,36 @@ class Puppeteer extends Helper {
 
 
   async _before() {
-    return this._startBrowser();
+    if (this.options.restart && !this.options.manualStart) return this._startBrowser();
+    if (!this.isRunning && !this.options.manualStart) return this._startBrowser();
+    return this.browser;
   }
 
   async _after() {
-    return this._stopBrowser();
+    if (!this.isRunning) return;
+    if (this.options.restart) {
+      this.isRunning = false;
+      return this._stopBrowser();
+    }
+
+    if (this.options.keepBrowserState) return;
+
+    if (!this.options.keepCookies) {
+      this.debugSection('Session', 'cleaning cookies and localStorage');
+      await this.browser.deleteCookie();
+    }
+    await this.browser.execute('localStorage.clear();').catch((err) => {
+      if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
+    });
+    await this.closeOtherTabs();
+    return this.browser;
+  }
+
+  _afterSuite() {
+  }
+
+  _finishTest() {
+    if (!this.options.restart && this.isRunning) return this._stopBrowser();
   }
 
   /**
@@ -216,28 +249,42 @@ class Puppeteer extends Helper {
 
   async _startBrowser() {
     this.browser = await puppeteer.launch(this.puppeteerOptions);
-    this.browser.on('targetcreated', (target) => {
-      target.page().then((page) => {
-        if (!page) return;
-        this.withinLocator = null;
-        page.on('load', frame => this.context = page.$('body'));
-        page.on('console', (msg) => {
-          this.debugSection(msg.type(), msg.args().join(' '));
-          consoleLogStore.add(msg);
-        });
+    const targetCreatedHandler = (page) => {
+      if (!page) return;
+      this.withinLocator = null;
+      page.on('load', frame => this.context = page.$('body'));
+      page.on('console', (msg) => {
+        this.debugSection(msg.type(), msg.args().join(' '));
+        consoleLogStore.add(msg);
       });
+    };
+    this.browser.on('targetcreated', (target) => {
+      target.page().then(page => targetCreatedHandler(page));
     });
 
     this.browser.on('targetchanged', (target) => {
       this.debugSection('Url', target.url());
     });
 
-    this._setPage(await this.browser.newPage());
+    const existingPages = await this.browser.pages();
+    const mainPage = existingPages[0] || await this.browser.newPage();
+
+    if (existingPages.length) {
+      // Run the handler as it will not be triggered if the page already exists
+      targetCreatedHandler(mainPage);
+    }
+    this._setPage(mainPage);
 
     if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) {
       const dimensions = this.options.windowSize.split('x');
       await this.resizeWindow(parseInt(dimensions[0], 10), parseInt(dimensions[1], 10));
     }
+
+    this.isRunning = true;
+
+    if (this.options.userAgent) {
+      await this.page.setUserAgent(this.options.userAgent);
+    }
   }
 
   async _stopBrowser() {
@@ -552,7 +599,8 @@ class Puppeteer extends Helper {
   async closeCurrentTab() {
     const oldPage = this.page;
     await this.switchToPreviousTab();
-    return oldPage.close();
+    await oldPage.close();
+    return this._waitForAction();
   }
 
   /**
@@ -570,7 +618,8 @@ class Puppeteer extends Helper {
     otherPages.forEach((page) => {
       p = p.then(() => page.close());
     });
-    return p;
+    await p;
+    return this._waitForAction();
   }
 
   /**
@@ -582,6 +631,15 @@ class Puppeteer extends Helper {
    */
   async openNewTab() {
     this._setPage(await this.browser.newPage());
+    return this._waitForAction();
+  }
+
+  /**
+   * {{> ../webapi/grabNumberOfOpenTabs }}
+   */
+  async grabNumberOfOpenTabs() {
+    const pages = await this.browser.pages();
+    return pages.length;
   }
 
   /**
@@ -1140,10 +1198,11 @@ class Puppeteer extends Helper {
   /**
    * {{> ../webapi/saveScreenshot }}
    */
-  async saveScreenshot(fileName, fullPage = this.options.fullPageScreenshots) {
+  async saveScreenshot(fileName, fullPage) {
+    const fullPageOption = fullPage || this.options.fullPageScreenshots;
     const outputFile = path.join(global.output_dir, fileName);
     this.debug(`Screenshot is saving to ${outputFile}`);
-    return this.page.screenshot({ path: outputFile, fullPage, type: 'png' });
+    return this.page.screenshot({ path: outputFile, fullPage: fullPageOption, type: 'png' });
   }
 
   async _failed(test) {
@@ -1484,7 +1543,7 @@ async function proceedClick(locator, context = null, options = {}) {
     assertElementExists(els, locator, 'Clickable element');
   }
   await els[0].click(options);
-  await this._waitForAction();
+  return this._waitForAction();
 }
 
 async function findClickable(matcher, locator) {
diff --git a/lib/helper/REST.js b/lib/helper/REST.js
index d71cb1d2..cfa7034d 100644
--- a/lib/helper/REST.js
+++ b/lib/helper/REST.js
@@ -170,7 +170,6 @@ class REST extends Helper {
    * @param {object} headers
    */
   sendPostRequest(url, payload = {}, headers = {}) {
-    payload = typeof payload === 'object' ? JSON.stringify(payload) : payload;
     request = unirest.post(this._url(url)).headers(headers).send(payload);
     return this._executeRequest(request);
   }
@@ -187,7 +186,6 @@ class REST extends Helper {
    * @param {object} headers
    */
   sendPatchRequest(url, payload = {}, headers = {}) {
-    payload = typeof payload === 'object' ? JSON.stringify(payload) : payload;
     request = unirest.patch(this._url(url)).headers(headers).send(payload);
     return this._executeRequest(request);
   }
@@ -204,7 +202,6 @@ class REST extends Helper {
    * @param {object} headers
    */
   sendPutRequest(url, payload = {}, headers = {}) {
-    payload = typeof payload === 'object' ? JSON.stringify(payload) : payload;
     request = unirest.put(this._url(url)).headers(headers).send(payload);
     return this._executeRequest(request);
   }
diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js
index 54f2d44e..6c4d75d3 100644
--- a/lib/helper/WebDriverIO.js
+++ b/lib/helper/WebDriverIO.js
@@ -1701,6 +1701,13 @@ class WebDriverIO extends Helper {
     return client.newWindow('about:blank');
   }
 
+  /**
+   * {{> ../webapi/grabNumberOfOpenTabs }}
+   */
+  async grabNumberOfOpenTabs() {
+    const pages = await this.browser.getTabIds();
+    return pages.length;
+  }
 
   /**
    * {{> ../webapi/refreshPage }}
diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js
index a6266573..86fd3d3b 100644
--- a/lib/interfaces/bdd.js
+++ b/lib/interfaces/bdd.js
@@ -60,6 +60,11 @@ module.exports = function (suite) {
       }
       const test = new Test(title, fn);
       test.fullTitle = () => `${suite.title}: ${title}`;
+      test.injectDependencies = (deps) => {
+        Object.keys(deps).forEach((key) => {
+          test.inject[key] = deps[key];
+        });
+      }
       test.file = file;
       test.async = true;
       test.timeout(0);


Test patch:
diff --git a/test/data/rest/db.json b/test/data/rest/db.json
index 838c80ee..9f5474ac 100644
--- a/test/data/rest/db.json
+++ b/test/data/rest/db.json
@@ -1,1 +1,1 @@
-{"comments":[],"posts":[{"id":1,"title":"json-server","author":"davert"}]}
\ No newline at end of file
+{"comments":[],"posts":[{"id":1,"title":"json-server","author":"davert"}]}
diff --git a/test/data/sandbox/ddt_test.ddt.js b/test/data/sandbox/ddt_test.ddt.js
index 6c67ab73..1c832046 100644
--- a/test/data/sandbox/ddt_test.ddt.js
+++ b/test/data/sandbox/ddt_test.ddt.js
@@ -22,3 +22,7 @@ Data(function* () {
 }).Scenario('Should log accounts3', (I, current) => {
   console.log(`Got changed login ${current[0]}`);
 });
+
+Data(['1', '2', '3']).Scenario('Should log array of strings', (I, current) => {
+  console.log(`Got array item ${current}`);
+});
diff --git a/test/helper/Nightmare_test.js b/test/helper/Nightmare_test.js
index 9b850877..0985bfff 100644
--- a/test/helper/Nightmare_test.js
+++ b/test/helper/Nightmare_test.js
@@ -156,4 +156,9 @@ describe('Nightmare', function () {
       assert.equal(url, nextUrl);
     });
   });
+
+  describe('#seeNumberOfElements', () => {
+    it('should return 1 as count', () => I.amOnPage('/')
+      .then(() => I.seeNumberOfElements('#area1', 1)));
+  });
 });
diff --git a/test/helper/Protractor_test.js b/test/helper/Protractor_test.js
index 896d8db5..570e0c7c 100644
--- a/test/helper/Protractor_test.js
+++ b/test/helper/Protractor_test.js
@@ -380,8 +380,14 @@ describe('Protractor', function () {
     });
   });
 
-  describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs', () => {
+  describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs, #grabNumberOfOpenTabs', () => {
+    it('should only have 1 tab open when the browser starts and navigates to the first page', () => I.amOnPage('/')
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
+
     it('should switch to next tab', () => I.amOnPage('/')
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1))
       .then(() => I.click('Get More Options'))
       .then(() => I.seeCurrentUrlEquals('/#/options'))
       .then(() => I.openNewTab())
@@ -391,7 +397,9 @@ describe('Protractor', function () {
       .then(() => I.switchToPreviousTab())
       .then(() => I.seeCurrentUrlEquals('/#/options'))
       .then(() => I.switchToNextTab())
-      .then(() => I.seeCurrentUrlEquals('/#/info')));
+      .then(() => I.seeCurrentUrlEquals('/#/info'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2)));
 
     it('should assert when there is no ability to switch to next tab', () => I.amOnPage('/')
       .then(() => I.click('Get More Options'))
@@ -414,10 +422,14 @@ describe('Protractor', function () {
       .then(() => I.seeInCurrentUrl('#/info'))
       .then(() => I.openNewTab())
       .then(() => I.amOnPage('/'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2))
       .then(() => I.seeInCurrentUrl('#/'))
       .then(() => I.dontSeeInCurrentUrl('#/info'))
       .then(() => I.closeCurrentTab())
-      .then(() => I.seeInCurrentUrl('#/info')));
+      .then(() => I.seeInCurrentUrl('#/info'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
 
     it('should close other tabs', () => I.amOnPage('/')
       .then(() => I.click('Get more info!'))
@@ -426,15 +438,23 @@ describe('Protractor', function () {
       .then(() => I.amOnPage('/'))
       .then(() => I.openNewTab())
       .then(() => I.amOnPage('/'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 3))
       .then(() => I.click('Get More Options'))
       .then(() => I.seeCurrentUrlEquals('/#/options'))
       .then(() => I.closeOtherTabs())
-      .then(() => I.seeCurrentUrlEquals('/#/options')));
+      .then(() => I.seeCurrentUrlEquals('/#/options'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
 
     it('should open new tab', () => I.amOnPage('/')
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1))
       .then(() => I.openNewTab())
       .then(() => I.amOutsideAngularApp())
-      .then(() => I.seeInCurrentUrl('about:blank')));
+      .then(() => I.seeInCurrentUrl('about:blank'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2)));
 
     it('should switch to previous tab', () => I.amOnPage('/')
       .then(() => I.click('Get more info!'))
@@ -443,7 +463,6 @@ describe('Protractor', function () {
       .then(() => I.seeInCurrentUrl('/#/'))
       .then(() => I.switchToPreviousTab())
       .then(() => I.wait(2))
-
       .then(() => I.seeInCurrentUrl('/#/info')));
   });
 
diff --git a/test/helper/Puppeteer_test.js b/test/helper/Puppeteer_test.js
index 6ea5f924..af94b06a 100644
--- a/test/helper/Puppeteer_test.js
+++ b/test/helper/Puppeteer_test.js
@@ -99,14 +99,23 @@ describe('Puppeteer', function () {
       .then(() => I.dontSee('Hovered', '#show')));
   });
 
-  describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs', () => {
+  describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs, #grabNumberOfOpenTabs', () => {
+    it('should only have 1 tab open when the browser starts and navigates to the first page', () => I.amOnPage('/')
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
+
     it('should switch to next tab', () => I.amOnPage('/info')
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1))
       .then(() => I.click('New tab'))
       .then(() => I.switchToNextTab())
-      .then(() => I.seeCurrentUrlEquals('/login')));
+      .then(() => I.seeCurrentUrlEquals('/login'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2)));
 
     it('should assert when there is no ability to switch to next tab', () => I.amOnPage('/')
       .then(() => I.click('More info'))
+      .then(() => I.wait(1)) // Wait is required because the url is change by previous statement (maybe related to #914)
       .then(() => I.switchToNextTab(2))
       .then(() => assert.equal(true, false, 'Throw an error if it gets this far (which it should not)!'))
       .catch((e) => {
@@ -117,8 +126,12 @@ describe('Puppeteer', function () {
       .then(() => I.click('New tab'))
       .then(() => I.switchToNextTab())
       .then(() => I.seeInCurrentUrl('/login'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2))
       .then(() => I.closeCurrentTab())
-      .then(() => I.seeInCurrentUrl('/info')));
+      .then(() => I.seeInCurrentUrl('/info'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
 
     it('should close other tabs', () => I.amOnPage('/')
       .then(() => I.openNewTab())
@@ -128,17 +141,30 @@ describe('Puppeteer', function () {
       .then(() => I.switchToNextTab())
       .then(() => I.seeInCurrentUrl('/login'))
       .then(() => I.closeOtherTabs())
-      .then(() => I.seeInCurrentUrl('/login')));
+      .then(() => I.seeInCurrentUrl('/login'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
 
     it('should open new tab', () => I.amOnPage('/info')
       .then(() => I.openNewTab())
-      .then(() => I.seeInCurrentUrl('about:blank')));
+      .then(() => I.seeInCurrentUrl('about:blank'))
+      .then(() => I.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2)));
 
     it('should switch to previous tab', () => I.amOnPage('/info')
       .then(() => I.openNewTab())
       .then(() => I.seeInCurrentUrl('about:blank'))
       .then(() => I.switchToPreviousTab())
       .then(() => I.seeInCurrentUrl('/info')));
+
+    it('should assert when there is no ability to switch to previous tab', () => I.amOnPage('/info')
+      .then(() => I.openNewTab())
+      .then(() => I.waitInUrl('about:blank'))
+      .then(() => I.switchToPreviousTab(2))
+      .then(() => I.waitInUrl('/info'))
+      .catch((e) => {
+        assert.equal(e.message, 'There is no ability to switch to previous tab with offset 2');
+      }));
   });
 
   describe('popup : #acceptPopup, #seeInPopup, #cancelPopup, #grabPopupText', () => {
diff --git a/test/helper/WebDriverIO_test.js b/test/helper/WebDriverIO_test.js
index 4a6d98e8..f5f6776a 100644
--- a/test/helper/WebDriverIO_test.js
+++ b/test/helper/WebDriverIO_test.js
@@ -321,23 +321,39 @@ describe('WebDriverIO', function () {
       .then(() => wd.dontSee('Hovered', '#show')));
   });
 
-  describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs', () => {
+  describe('#switchToNextTab, #switchToPreviousTab, #openNewTab, #closeCurrentTab, #closeOtherTabs, #grabNumberOfOpenTabs', () => {
+    it('should only have 1 tab open when the browser starts and navigates to the first page', () => wd.amOnPage('/')
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
+
     it('should switch to next tab', () => wd.amOnPage('/info')
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1))
       .then(() => wd.click('New tab'))
       .then(() => wd.switchToNextTab())
-      .then(() => wd.waitInUrl('/login')));
+      .then(() => wd.waitInUrl('/login'))
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2)));
+
     it('should assert when there is no ability to switch to next tab', () => wd.amOnPage('/')
       .then(() => wd.click('More info'))
+      .then(() => wd.wait(1)) // Wait is required because the url is change by previous statement (maybe related to #914)
       .then(() => wd.switchToNextTab(2))
+      .then(() => assert.equal(true, false, 'Throw an error if it gets this far (which it should not)!'))
       .catch((e) => {
         assert.equal(e.message, 'There is no ability to switch to next tab with offset 2');
       }));
+
     it('should close current tab', () => wd.amOnPage('/info')
       .then(() => wd.click('New tab'))
       .then(() => wd.switchToNextTab())
-      .then(() => wd.waitInUrl('/login'))
+      .then(() => wd.seeInCurrentUrl('/login'))
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2))
       .then(() => wd.closeCurrentTab())
-      .then(() => wd.waitInUrl('/info')));
+      .then(() => wd.seeInCurrentUrl('/info'))
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
 
     it('should close other tabs', () => wd.amOnPage('/')
       .then(() => wd.openNewTab())
@@ -347,16 +363,22 @@ describe('WebDriverIO', function () {
       .then(() => wd.switchToNextTab())
       .then(() => wd.seeInCurrentUrl('/login'))
       .then(() => wd.closeOtherTabs())
-      .then(() => wd.seeInCurrentUrl('/login')));
+      .then(() => wd.seeInCurrentUrl('/login'))
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 1)));
 
     it('should open new tab', () => wd.amOnPage('/info')
       .then(() => wd.openNewTab())
-      .then(() => wd.waitInUrl('about:blank')));
+      .then(() => wd.waitInUrl('about:blank'))
+      .then(() => wd.grabNumberOfOpenTabs())
+      .then(numPages => assert.equal(numPages, 2)));
+
     it('should switch to previous tab', () => wd.amOnPage('/info')
       .then(() => wd.openNewTab())
       .then(() => wd.waitInUrl('about:blank'))
       .then(() => wd.switchToPreviousTab())
       .then(() => wd.waitInUrl('/info')));
+
     it('should assert when there is no ability to switch to previous tab', () => wd.amOnPage('/info')
       .then(() => wd.openNewTab())
       .then(() => wd.waitInUrl('about:blank'))
diff --git a/test/rest/REST_test.js b/test/rest/REST_test.js
index 4e5f3482..53ac8fda 100644
--- a/test/rest/REST_test.js
+++ b/test/rest/REST_test.js
@@ -46,22 +46,30 @@ describe('REST', () => {
       response.body.name.should.eql('davert');
     }));
 
-    it('should send PATCH requests', () => I.sendPatchRequest('/user', { email: 'user@user.com' }).then((response) => {
-      const body = JSON.parse(Object.keys(response.body)[1]);
-      body.email.should.eql('user@user.com');
+    it('should send PATCH requests: payload format = json', () => I.sendPatchRequest('/user', { email: 'user@user.com' }).then((response) => {
+      response.body.email.should.eql('user@user.com');
+    }));
+    it('should send PATCH requests: payload format = form urlencoded', () => I.sendPatchRequest('/user', 'email=user@user.com').then((response) => {
+      response.body.email.should.eql('user@user.com');
     }));
 
-    it('should send POST requests', () => I.sendPostRequest('/user', { name: 'john' }).then((response) => {
-      const body = JSON.parse(Object.keys(response.body)[0]);
-      body.name.should.eql('john');
+    it('should send POST requests: payload format = json', () => I.sendPostRequest('/user', { name: 'john' }).then((response) => {
+      response.body.name.should.eql('john');
+    }));
+    it('should send POST requests: payload format = form urlencoded', () => I.sendPostRequest('/user', 'name=john').then((response) => {
+      response.body.name.should.eql('john');
     }));
 
-    it('should send PUT requests', () => I.sendPutRequest('/posts/1', { author: 'john' }).then((response) => {
-      const body = JSON.parse(Object.keys(response.body)[0]);
-      body.author.should.eql('john');
+    it('should send PUT requests: payload format = json', () => I.sendPutRequest('/posts/1', { author: 'john' }).then((response) => {
+      response.body.author.should.eql('john');
+      return I.sendGetRequest('/posts/1').then((response) => {
+        response.body.author.should.eql('john');
+      });
+    }));
+    it('should send PUT requests: payload format = form urlencoded', () => I.sendPutRequest('/posts/1', 'author=john').then((response) => {
+      response.body.author.should.eql('john');
       return I.sendGetRequest('/posts/1').then((response) => {
-        const body = JSON.parse(Object.keys(response.body)[0]);
-        body.author.should.eql('john');
+        response.body.author.should.eql('john');
       });
     }));