I would like to asynchronously create a test user and a test project over an XHR request before a collection of tests runs.
I expected one of the following to work:
BeforeSuite((I) => new Promise((resolve) => setTimeout(resolve, 5000)))
Before((I) => new Promise((resolve) => setTimeout(resolve, 5000)))
BeforeSuite(function* (I) {
yield new Promise((resolve) => setTimeout(resolve, 5000))
})
Before(function* (I) {
yield new Promise((resolve) => setTimeout(resolve, 5000))
})
Instead, the tests progress without any blocking/waiting. Is there any approach I can take to postbone test execution until my prerequisits are fulfilled?
exports.config = {
tests: './tests/src/end-to-end/**/*.[tj]s',
timeout: 10000,
output: './tests/output/end-to-end',
helpers: {
WebDriverIO: {
url: process.env.GUI_HOST,
browser: 'chrome',
restart: true,
desiredCapabilities: {
browserName: 'chrome',
},
host: 'selenium',
port: 4444,
},
TestUser: {
"require": "./tests/config/codecept/TestUser.js",
},
TestBusinessEntity: {
"require": "./tests/config/codecept/TestBusinessEntity.js",
},
},
include: {
I: './tests/config/codecept/custom_steps.js',
},
bootstrap: false,
mocha: {},
name: 'byll-gui',
};
Ok, promises are stored in a global promise chain. There is a way to access it (I will post it below, however it is not documented and not recommended to do so). To attach a promise to a promise chain it should be implemented as a method inside a helper and called inside a test:
BeforeSuite((I) => {
I.doSomethingBeforeStart();
});
doSomethingBeforeStart should be defined inside a custom helper as a regular method returning a promise:
doSomething() {
new Promise((resolve) => setTimeout(resolve, 5000))
}
Once the helper is enabled you can use its method inside Before/BeforeSuite.
Alternatively you can access a promise chain on your own
```js
const recorder = require('codeceptjs/lib/recorder');
Before(function() {
recorder.add(new Promise((resolve) => setTimeout(resolve, 5000)));
});
```
I think it would be a good idea to implement handling of promises in Before/After in a next version. So probably its a good idea for a feature
Awesome, thanks for your quick response, @DavertMik!
I just looked into this again, and have 2 questions:
The second version (accessing the promise chain) does not work for me:
// example2.js
const recorder = require('codeceptjs/lib/recorder')
Feature('Example2')
Before(function (I) {
recorder.add(new Promise((resolve) => setTimeout(() => {
resolve()
console.log('5 secs are over')
}, 5000)))
I.say('Before should have waited for 5 secs')
})
Scenario('Test scenario', function* (I) {
I.say('Starting test')
I.amOnPage(`/`)
})
Output:
> node_modules/.bin/codeceptjs run --steps . example2.js
CodeceptJS v0.5.1
Using test root "/Users/philipstanislaus/Code/basement-startup/byll-gui"
Example2 --
Test scenario
Before should have waited for 5 secs
Starting test
• I am on page "/"
✓ OK in 1199ms
OK | 1 passed // 2s
5 secs are over
("5 secs are over" should have come first, instead the suite passes in 2s)
a) What is wrong here?
Your first method works as follows:
// Example.js
class Example extends Helper {
fetchUser() {
return new Promise((resolve) => setTimeout(() => {
resolve({ name: 'Cleopatra' })
console.log('5 secs are over')
}, 5000))
}
}
module.exports = Example
// example1.js
Feature('Example1')
Before(function (I) {
I.fetchUser()
I.say('Before should have waited for 5 secs')
})
Scenario('Test scenario', function* (I) {
I.say(`Starting test`)
I.amOnPage(`/`)
})
Output:
> node_modules/.bin/codeceptjs run --steps . example1.js
CodeceptJS v0.5.1
Using test root "/Users/philipstanislaus/Code/basement-startup/byll-gui"
Example1 --
Test scenario
• I fetch user
5 secs are over
Before should have waited for 5 secs
Starting test
• I am on page "/"
✓ OK in 993ms
OK | 1 passed // 7s
Yet it does not seem to work with generator functions as tests do, is that expected?
Example:
// Example.js
// same as above...
// example1.js
Feature('Example1')
let user
Before(function* (I) {
yield user = I.fetchUser()
I.say('Before should have waited for 5 secs')
})
Scenario('Test scenario', function* (I) {
I.say(`Starting test with user ${user}`)
I.amOnPage(`/`)
})
Output:
> node_modules/.bin/codeceptjs run --steps . example1.js
CodeceptJS v0.5.1
Using test root "/Users/philipstanislaus/Code/basement-startup/byll-gui"
Example1 --
Test scenario
Starting test with user undefined
• I am on page "/"
✓ OK in 1163ms
OK | 1 passed // 2s
(Does not even output "Before should have waited for 5 secs" or "5 secs are over")
@philipstanislaus Hey, have you found a way yet to achieve asynchronous calls in before hooks?
@sveneisenschmidt unfortunately I have not, I used a test to achieve what I wanted. Let us know if you find a solution!
@DavertMik Hey ☺️, do you think this is something we can see as a feature in one of the upcoming releases?
I think it would be a good idea to implement handling of promises in Before/After in a next version. So probably its a good idea for a feature
Otherwise, I haven't had a look into the internal code yet, but I would be glad to help out as well. Maybe you can point me to a specific part of the code that needs to be extended to support promises and/or generators in Before/After hooks.
Any feedback is appreciated, have a nice day ☺️
@DavertMik @philipstanislaus My lovely co-workers found a solution. Will post an update tomorrow.
@philipstanislaus
node version: v8.1.3
Output:
> codeceptjs run --steps │Only local connections are allowed.
│09:48:20.350 INFO - Detected dialect: OSS
CodeceptJS v0.6.3 │09:48:20.353 INFO - Done: [new session: Capabilities [{rotatable=true, locationContextEnabled=true, loggingPrefs=org.openqa.sel
Using test root "/home/seisenschmidt/Projects/workshop" │enium.logging.LoggingPreferences@50746fb3, browserName=chrome, javascriptEnabled=true, chromeOptions={args=[]}, handlesAlerts=t
│rue, requestOrigins={name=webdriverio, version=4.6.2, url=http://webdriver.io}}]]
Title -- │09:48:20.379 INFO - Executing: [script wait: 1000])
test something │09:48:20.379 INFO - Executing: [set window size])
• I am on page "/100/de_DE/hotel/show?h[hotelId]=5001" │09:48:20.381 INFO - Executing: [get: https://express.trivago.com/100/de_DE/hotel/show?h[hotelId]=5001])
• I grab text from "body h1" │09:48:20.385 INFO - Done: [script wait: 1000]
[ 'Best Western Hotel Goldenes Rad', '' ] │09:48:21.661 INFO - Done: [get: https://express.trivago.com/100/de_DE/hotel/show?h[hotelId]=5001]
Interative debug session started │09:48:21.666 INFO - Executing: [get current url])
Use JavaScript syntax to try steps in action │09:48:21.841 INFO - Done: [set window size]
Press ENTER to continue │09:48:21.864 INFO - Done: [get current url]
I. │09:48:21.882 INFO - Executing: [find elements: By.cssSelector: body h1])
Exiting interactive shell.... │09:48:21.933 INFO - Done: [find elements: By.cssSelector: body h1]
✓ OK
Scenario:
Feature('Title');
Before(async function(I, Page) {
Page.LandingPage.open(100, 'de_DE', 5001);
const text = await I.grabTextFrom('body h1');
console.log(text);
});
Scenario('test something', function (I, Page) {
pause();
});
async/await can be used in Before/After
Generators support will be in next releases https://github.com/Codeception/CodeceptJS/issues/206
Most helpful comment
Ok, promises are stored in a global promise chain. There is a way to access it (I will post it below, however it is not documented and not recommended to do so). To attach a promise to a promise chain it should be implemented as a method inside a helper and called inside a test:
doSomethingBeforeStartshould be defined inside a custom helper as a regular method returning a promise:Once the helper is enabled you can use its method inside Before/BeforeSuite.
Alternatively you can access a promise chain on your own
```js
const recorder = require('codeceptjs/lib/recorder');
Before(function() {
recorder.add(new Promise((resolve) => setTimeout(resolve, 5000)));
});
```