acuminous / yadda

A BDD javascript library
412 stars 73 forks source link

promise didn't work with nodeJS #237

Closed ZHChen8397 closed 6 years ago

ZHChen8397 commented 6 years ago

Hi, I got a problem when I use promise in my nodeJS project. Here's my code

cam.js `

var assert = require('assert')
var PythonShell = require('python-shell');
var English = require('yadda').localisation.English
let returnValue

module.exports = English.library()
  .given('I run my python code', function (next) {
    openCam()
    .then((result)=>{
      console.log(result)
    })
})

.when('camera detect something and close the camera', function () {
    // do something
})

.then('It should return value that I respect', function (num) {
    // do something
})

function openCam(){
  return new Promise((resolve,reject)=>{
    var options = {
      scriptPath: './pyforJS'
      };
      var pyshell = new PythonShell('cam.py',options);
      pyshell.on('message', function (result) {
          returnValue = result
          if(returnValue) resolve(result)
          else reject(result)
      });
  })
}

`

In my test, I expect that when I run my test, the test will stop at given step, open the camera, and wait return value, then the test go to when, then step to finish the test. But actually when I run the test, it will run given,when,then, then open my camera. and the console log in my given step will be undefined(since my camera open last step and didn't close yet)

Is there anything I do the wrong step? Thanks!

cressie176 commented 6 years ago

Hi @ZHChen8397

I'll try to take a look this evening (GMT).

ZHChen8397 commented 6 years ago

@cressie176 Thanks for the quick response. Here's my code to run yadda `

"use strict";

var path = require('path');
var Yadda = require('yadda');
Yadda.plugins.mocha.StepLevelPlugin.init();

new Yadda.FeatureFileSearch('./test/unit/features').each(function(file) {
     // console.log(file)
    featureFile(file, function(feature) {

        var libraries = require_feature_libraries(feature);
        var yadda = Yadda.createInstance(libraries);

        scenarios(feature.scenarios, function(scenario) {
            steps(scenario.steps, function(step) {
                yadda.run(step);
            });
        });
    });
});

function require_feature_libraries(feature) {
    // console.log(`feature ${JSON.stringify(feature,null,2)}`)
    return feature.annotations.step.split(', ').reduce(require_library, []);
}

function require_library(libraries, library) {
    return libraries.concat(require('./test/unit/steps/' + library));
}

` The second point that you mentioned, I didn't use next to do asyn, I forgot to delete it The code is actually like this

`

module.exports = English.library()
    .given('I run my python code', function () {
    openCam()
    .then((result)=>{
        console.log(result)
    })
})

`

the other code is the same as above And I tried the Yadda User Guide

`

.given('I run my python code', function () {
    return new Promise((resoleve,reject)=>{
        var options = {
            scriptPath: './pyforJS'
        };
        var pyshell = new PythonShell('cam.py',options);
        pyshell.on('message', function (result) {
            console.log(result)
            if(result) resolve(result)
            else reject(result)
        });
    })
})

` and still didn't work.

cressie176 commented 6 years ago

I think there's a return missing from your step...

module.exports = English.library()
  .given('I run my python code', function () {
    return openCam()
      .then((result)=>{
        console.log(result)
      })
  })
cressie176 commented 6 years ago

I've updated the promise example, wrapping the step code in setTimeouts so they're not executing synchronously.

Providing the steps all return promises the example works. If I omit a return, then Yadda doesn't know to wait for the promise to resolve and will move onto the next step immediately, causing the test to fail.

I suspect this is your issue.

ZHChen8397 commented 6 years ago

@cressie176 It works very fine when I totally copy your code and run the test. But when I tried to run multiple libraies, It will run only one test then the test will be finish

`

    new Yadda.FeatureFileSearch('./test/unit/features').each(function(file) {
        featureFile(file, function(feature) {

        var libraries = require_feature_libraries(feature);
        var yadda = Yadda.createInstance(libraries);

        scenarios(feature.scenarios, function(scenario) {
            steps(scenario.steps, function(step,done) {
                yadda.run(step,done);
            });
        });
    });
});

`

here's my dir --test ----unit --------features ------------bottle.feature (the example) ------------cam.feature (the feature I wrote) ------------detect.feature --------steps ------------bottle.js ------------cam.js ------------detect.js the test will run only bottls.feature and bottle.js

Besides, if I didn't add 'done' when I run my yadda, it will just go through the features and finish the test. It means that even if you assert fail in your test, you got all pass in your terminal. Is that a bug or I misunderstand how to use the done?

cressie176 commented 6 years ago

I just added a second feature (cups) to the promises example. Both run.

If I change one of the expected values the tests report a failure in the terminal.

  Mocha Asynchronous Thenable Example Using Bottles
    A bottle falls from the wall
      ✓ Given a 6 foot wall (106ms)
      ✓ Given 100 green bottles are standing on the wall (103ms)
      ✓ when 1 green bottle accidentally falls (107ms)
      ✓ then there are 99 green bottles standing on the wall 
    No bottles are left
      ✓ Given a 6 foot wall (102ms)
      ✓ Given 1 green bottles are standing on the wall (103ms)
      ✓ when 1 green bottle accidentally falls (104ms)
      ✓ then there are 0 green bottles standing on the wall 
    Bottles are reset
      - Given a 6 foot wall
      - Given there are no green bottles
      - when 5 minutes has elapsed
      - then there are 100 green bottles standing on the wall
    100 bottles are standing on a wall
      ✓ Given a 6 foot wall (101ms)
      ✓ Given 100 green bottles are standing on the wall (104ms)
      ✓ when 1 green bottle accidentally falls (102ms)
      1) then there are 98 green bottles standing on the wall
    99 bottles are standing on a wall
      ✓ Given a 6 foot wall (103ms)
      ✓ Given 99 green bottles are standing on the wall (104ms)
      ✓ when 1 green bottle accidentally falls (104ms)
      ✓ then there are 98 green bottles standing on the wall 
    10 bottles are standing on a wall
      ✓ Given a 6 foot wall (101ms)
      ✓ Given 10 green bottles are standing on the wall (104ms)
      ✓ when 1 green bottle accidentally falls (104ms)
      ✓ then there are 9 green bottles standing on the wall 

  Mocha Asynchronous Thenable Example Using Cups
    A cup falls from the wall
      ✓ Given a 6 foot wall (102ms)
      ✓ Given 100 green cups are standing on the wall (101ms)
      ✓ when 1 green cup accidentally falls (103ms)
      ✓ then there are 99 green cups standing on the wall 
    No cups are left
      ✓ Given a 6 foot wall (105ms)
      ✓ Given 1 green cups are standing on the wall (105ms)
      ✓ when 1 green cup accidentally falls (102ms)
      ✓ then there are 0 green cups standing on the wall 
    Bottles are reset
      - Given a 6 foot wall
      - Given there are no green cups
      - when 5 minutes has elapsed
      - then there are 100 green cups standing on the wall
    100 cups are standing on a wall
      ✓ Given a 6 foot wall (107ms)
      ✓ Given 100 green cups are standing on the wall (103ms)
      ✓ when 1 green cup accidentally falls (104ms)
      2) then there are 98 green cups standing on the wall
    99 cups are standing on a wall
      ✓ Given a 6 foot wall (101ms)
      ✓ Given 99 green cups are standing on the wall (104ms)
      ✓ when 1 green cup accidentally falls (104ms)
      ✓ then there are 98 green cups standing on the wall 
    10 cups are standing on a wall
      ✓ Given a 6 foot wall (103ms)
      ✓ Given 10 green cups are standing on the wall (101ms)
      ✓ when 1 green cup accidentally falls (106ms)
      ✓ then there are 9 green cups standing on the wall 

  38 passing (3s)
  8 pending
  2 failing
ZHChen8397 commented 6 years ago

Sure it is correct. But I am using .feature to .js, like the example mocha-multi-library and it didn't work

cressie176 commented 6 years ago

I've updated https://github.com/acuminous/yadda/tree/issue-237/examples/mocha-async-thenable to use promises, multiple features and multiple steps. I suggest seeing if this works for you then step by step (no pun intended) replacing my code with yours.

ZHChen8397 commented 6 years ago

@cressie176 It works! Thanks for telling me how to solve this problem so patiently! I'll try to figure out why the last version I wrote didn't work. Thanks again!

ZHChen8397 commented 6 years ago

I think I find why I only run one feature. After assert something, you need to resolve the result, like

assert.equal(actual,expect)
resolve(true)

then it'll run next test correctly

cressie176 commented 6 years ago

Sorry, I just realised my last example was misleading. I had an extra "cup_type" step parameter...

.then("there (?:are|are still) $NUM green cups standing on the wall", function(number_of_cups, cup_type) {
  assert.equal(number_of_cups, wall.cups);
});

Because of the mismatch between number of parsed arguments ($NUM) and number of step arguments (number_of_cups, cup_type), Yadda assumed that the test was asynchronous, which is why you had to resolve the promise.

Without the extra parameter you should be able to do

.then("there (?:are|are still) $NUM green cups standing on the wall", function(number_of_cups) {
  assert.equal(number_of_cups, wall.cups);
});

To re-iterate any step can be synchronous, use callbacks or use promises.

// Synchronous step
.then("there (?:are|are still) $NUM green cups standing on the wall", function(number_of_cups) {
  assert.equal(number_of_cups, wall.cups);
});
// Asynchronous step using callbacks
.then("there (?:are|are still) $NUM green cups standing on the wall", function(number_of_cups, next) {
  assert.equal(number_of_cups, wall.cups);
  next();
});
// Asynchronous step using promises
.then("there (?:are|are still) $NUM green cups standing on the wall", function(number_of_cups) {
  return new Promise(function(resolve, reject) {
    assert.equal(number_of_cups, wall.cups);
    resolve(true);
  })
});
ZHChen8397 commented 6 years ago

Thanks for telling me that! My test could be better and more complete now!!

cressie176 commented 6 years ago

Glad to help. OK to close the issue?

ZHChen8397 commented 6 years ago

Sure, Thanks very much!