Decorate step implementation in Cucumber-js - javascript

Introduction
Im using https://github.com/cucumber/cucumber-js in typescript. Sample of code implementation looks like average cucumber implementation:
import {
Given,
Then,
When
} from 'cucumber'
Given(`Page is up and running`, function(this: World) {
someFunction()
})
Goal
What I would like to achieve is to be able to somehow decorate Given, so that:
some action can be invoked before code inside Given. Lets say, for simple example, I would like to print first Given argument (so Page is up and running).
there would be no need to modify existing step implementation.
Each step got two alias function definitions in index.d.ts, e.g.:
export function Given(pattern: RegExp | string, code: StepDefinitionCode): void;
export function Given(pattern: RegExp | string, options: StepDefinitionOptions, code: StepDefinitionCode): void;

In the original version of CucumberJS, there were more hooks available including beforeStep. However, there is a way to achieve this functionality in versions of the framework such as 6.0.5 using setDefinitionFunctionWrapper. Example:
setDefinitionFunctionWrapper(fn => async function step(...args) {
//
// do something before the step definition runs.
//
const result = await fn.apply(this, args);
//
// do something after the step definition runs.
//
return result;
});
Online Example: https://testjam.io/?p=O2wodDVOveAMdA9HKMKy
Known limitations and caveats:
This function will wrap every step definition AND hook.
This particular implementation assumes that the step definition or hook is not using a callback.
I'm not sure how to make callbacks and promises work here at the same time, it may be possible but you'll have to manage that yourself by possibly inspecting the type of the last parameter and then handling it from there.
Update: The beforeStep and afterStep hooks are available in CucumberJS 7.x and later

Related

Jest can't find module

I am just starting out with Jest, and I am trying to test this code that will change the textContent of an element after 1000ms:
const subtext = document.querySelector('.subtext');
function delayChangeText() {
setTimeout(() => {
subtext.textContent = "Dev";
}, 1000);
}
subtext.addEventListener('load', delayChangeText);
This is what Jest returns:
FAIL js/app.test.js
● Test suite failed to run
Cannot find module './delayChangeText' from 'js/app.test.js'
> 1 | const delayChangeText = require('./delayChangeText');
| ^
2 |
3 | test('Change the text after 1000 seconds', () => {
4 | expect(delayChangeText().toBe(subtext.textContent = "Dev"));
at Resolver.resolveModule (node_modules/jest-resolve/build/resolver.js:311:11)
at Object.<anonymous> (js/app.test.js:1:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 0.846 s
I am still pretty new to testing, I'm confident I made a pretty simple goof. Any help is much appreciated. Best regards.
The zeroth rule of testing is:
Code must be written such that it is testable
Not all code can be tested. Sometimes you have to change how your real code is written so that a testing framework can get its hands on the code.
I can see one or two critical problems.
First: I assume you didn't include the full content of your application, but it does not look like your app code exports the delayChangeText function, which means that other modules (such as your test suite) can't import it.
You may need to do something like module.exports = delayChangeText, or export default delayChangeText in your app code.
Second: your function is not a pure function. That is, it depends on stuff that's not passed to it explicitly, namely it expects that subtext is defined within its execution context.
It's not strictly required that all your functions be pure functions, and indeed sometimes it's not possible. But pure functions are usually much easier to test (as well as being easier to design and implement). Here's a pure version of your function:
function delayChangeText(element) {
setTimeout(() => {
element.textContent = "Dev";
}, 1000);
}
You don't have to convert this to a pure function, but your code will break in the test unless your test suite takes steps to ensure that subtext.textContent doesn't throw -- if subtext is undefined, it will throw.
This is important for another reason: if this module's default export is the delayChangeText function, then it's probably not appropriate for the preceding subtext assignment to even be in the file. Which means that fixing the first problem ("it's not being exported") will naturally result in converting the function to a pure function. If you really want to avoid that, you can: you'll probably have to change how the function is imported in the test suite, to this:
const { delayChangeText } = require('./delayChangeText');
Finally (and you didn't ask about this -- yet): you probably don't want this test to have to actually wait 1000 ms to test this function. Jest has some tools for manipulating the global timer functions, which will allow you to validate the function without having to wait.

Cypress how to store values yielded by a function into a variable

This has been bugging me from a long time. I am not well versed with javascript. Here it goes:
How do i store return value of a function into a variable:
lenValue = cy.get(selector).children().length
Above line of code returns undefined But when i try following in cypress test runner console then I get a valid output:
cy.$$(selector).children().length --> gives me correct number
How to return value from inside a then function and catch it to reuse later:
file1.js
function a(selector, attrName){
cy.get(selector).then(function ($el){
return $el.attr(attrName));
}
file2.js
state = file1Obj.a('#name','name')
What you're doing makes complete sense, but simply put, you cannot. (per the docs).
https://docs.cypress.io/guides/core-concepts/variables-and-aliases/#Return-Values
You can, however, use aliases to accomplish what (I think) you're after.
https://docs.cypress.io/guides/core-concepts/variables-and-aliases/#Aliases
#aeischeid shows you the wrong way to do it.
His code works only for a static site, but web pages are rarely static. As soon as API fetches are involved, lucky timing goes out the window and the test bombs.
This is why Cypress commands have automatic retry. Otherwise we could just build tests with jQuery.
Since cy.$$(selector).children().length --> gives me correct number, use that inside the helper function.
function a(selector, attrName) {
return cy.$$(selector).attr(attrName); // jQuery methods used
}
Or
function a(selector, attrName) {
return Cypress.$(selector).attr(attrName); // jQuery methods used
}
But be aware that jQuery only handles static pages, it does not retry if the attribute that you want to query arrives slowly.
For that use a command
cy.get('#name')
.should('have.attr', 'name') // retries until name exists
.then(name => { // guaranteed to have a value
// use name here
})
Here is a example from a cypress test I have that seems pretty relevant
let oldDescription;
cy.get('input#description').should(($input) => {
oldDescription = $input.val();
});
let randomDescription = Math.random().toString(36).substring(7);
cy.get('input#description').clear().type(randomDescription);
cy.get('input#description')
.parents('.ant-table-row')
.contains('Save').click();
cy.get('input#description').should('not.exist');
cy.contains(`${randomDescription}`);
cy.contains(`${oldDescription}`).should('not.exist');
because oldDescription is set inside of an asyncronous callback it isn't safe to expect it to be set, farther down the code outside of that callback, but in a lot of cases with cypress you end up having some other .get call or thing that waits, effectively pausing the code long enough that you can get away with not worrying about it.

javascript: module calls a function in the file that requires the module

My first time writing a js library. The library is intended to execute, at specific times, functions in the file that required the library. Kind of like Angular executes user implemented hooks such as $onInit, except that, in my case, user can define an arbitrary number of functions to be called by my library. How can I implement that?
One way I have in mind is to define a registerFunction(name, function) method, which maps function names to implementations. But can user just give me an array of names and I automatically register the corresponding functions for them?
Unless you have a specific requirement that it do so, your module does not need to know the names of the functions it is provided. When your module invokes those functions, it will do so by acting on direct references to them rather than by using their names.
For example:
// my-module.js
module.exports = function callMyFunctions( functionList ) {
functionList.forEach( fn => fn() )
}
// main application
const myFunc1 = () => console.log('Function 1 executing')
const myFunc2 = () => console.log('Function 2 executing')
const moduleThatInvokesMyFunctions = require('./my-module.js')
// instruct the module to invoke my 2 cool functions
moduleThatInvokesMyFunctions([ myFunc1, myFunc2 ])
//> Function 1 executing
//> Function 2 executing
See that the caller provides direct function references to the module, which the module then uses -- without caring or even knowing what those functions are called. (Yes, you can obtain their names by inspecting the function references, but why bother?)
If you want a more in-depth answer or explanation, it would help to know more about your situation. What environment does your library target: browsers? nodejs? Electron? react-native?
The library is intended to execute, at specific times, functions in the file that required the library
The "at specific times" suggests to me something that is loosely event-based. So, depending on what platform you're targeting, you could actually use a real EventEmitter. In that case, you'd invent unique names for each of the times that a function should be invoked, and your module would then export a singleton emitter. Callers would then assign event handlers for each of the events they care about. For callers, that might look like this:
const lifecycleManager = require('./your-module.js')
lifecycleManager.on( 'boot', myBootHandler )
lifecycleManager.on( 'config-available', myConfigHandler )
// etc.
A cruder way to handle this would be for callers to provide a dictionary of functions:
const orchestrateJobs = require('./your-module.js')
orchestrateJobs({
'boot': myBootHandler,
'config-available': myConfigHandler
})
If you're not comfortable working with EventEmitters, this may be appealing. But going this route requires that you consider how to support other scenarios like callers wanting to remove a function, and late registration.
Quick sketch showing how to use apply with each function:
// my-module.js
module.exports = function callMyFunctions( functionList ) {
functionList.forEach( fn => fn.apply( thisValue, arrayOfArguments ) )
}
Note that this module still has no idea what names the caller has assigned to these functions. Within this scope, each routine bears the moniker "fn."
I get the sense you have some misconceptions about how execution works, and that's led you to believe that the parts of the program need to know the names of other parts of the program. But that's not how continuation-passing style works.
Since you're firing caller functions based on specific times, it's possible the event model might be a good fit. Here's a sketch of what that might look like:
// caller
const AlarmClock = require('./your-module.js')
function doRoosterCall( exactTime ) {
console.log('I am a rooster! Cock-a-doodle-doo!')
}
function soundCarHorn( exactTime ) {
console.log('Honk! Honk!')
}
AlarmClock.on('sunrise', doRoosterCall)
AlarmClock.on('leave-for-work', soundCarHorn)
// etc
To accomplish that, you might do something like...
// your-module.js
const EventEmitter = require('events')
const singletonClock = new EventEmitter()
function checkForEvents() {
const currentTime = new Date()
// check for sunrise, which we'll define as 6:00am +/- 10 seconds
if(nowIs('6:00am', 10 * 1000)) {
singletonClock.emit('sunrise', currentTime)
}
// check for "leave-for-work": 8:30am +/- 1 minute
if(nowIs('8:30am', 60 * 1000)) {
singletonClock.emit('leave-for-work', currentTime)
}
}
setInterval( checkForEvents, 1000 )
module.exports = singletonClock
(nowIs is some handwaving for time-comparisons. When doing cron-like work, you should assume your heartbeat function will almost never be fired when the time value is an exact match, and so you'll need something to provide "close enough" comparisons. I didn't provide an impl because (1) it seems like a peripheral concern here, and (2) I'm sure Momentjs, date-fns, or some other package provides something great so you won't need to implement it yourself.

Change syntax for protractor step definitions from promises to async syntax

I have a protractor-cucumber framework whose step definitions are somewhat structured as per this: https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/step_definitions.md
I use a return and chain the promises together. Recently, I came across a different syntax called the async function. But, when I try to convert my step definitions to async, all the help files in the framework where I use say module.exports and require() display the following warning:
[ts] File is a CommonJS module; it may be converted to an ES6 module.
When I run test cases since I can't access these helper files due to the error my tests cases fail. Like, my page object files, I am not able to access them from my tests. I think they don't get exported like they used to.
Could someone please advice me as to how I can change my test cases to async syntax without breaking them? How do I resolve the above issue without disrupting my tests in a major way.
Adding code
Here is a step from my step definition before the change
let { Given, Then, When } = require('cucumber');
Given(/^I am on the "([^"]*)" page$/, function (home) {
home = this.url.FDI_HOME;
return browser.get(home);
});
Here is a step definition, after I change it to an async function
let { Given, Then, When } = require('cucumber');
Given(/^I am on the "([^"]*)" page$/, async function (home) {
home = this.url.HOME
await browser.get(home);
});
And I will change my other steps in similar fashion. Problem arises when I try to run the above step it fails saying that it is not able to access this.url.HOME. I have another file to supply URLs called the urls.js looks something like this
let targetStore = browser.params.store || 'bestbuy';
let FDI_HOST = browser.params.fdi;
module.exports = {
HOME Page: 'https://homepage.com',
Shop_Page: 'https://shop.com',
storeLink: `http://www.${targetStore}.com`,
};
I see three dots under the word "module.exports" in VS code and when I hover over it, it displays an error saying: [ts] File is a CommonJS module; it may be converted to an ES6 module.
I have tried to find a resolution to this but not been able to successfully make it. if I use the syntax as "async()=>{}" the test cases fails but when I use "async function(){}" then a few of the steps pass but not the other.
These are suggestions/hints. They visually indicate that vscode can perform an action to possibly refactor/improve your code, but they are not treated as errors.
You can disable them by adding "javascript.suggestionActions.enabled": false to your user/workspace settings.
Source: https://github.com/Microsoft/vscode/issues/47299

RxJS Testing Observable sequence without passing scheduler

I have problems attempting to test a piece of code that is similar to the following function.
Basically the question boils down to: is it possible to change the Scheduler for the debounce operator without passing a separate Scheduler to the function call?
The following example should explain the use case a bit more concrete. I am trying to test a piece of code similar to the following. I want to test the chain in the function (using a TestScheduler) without having to pass a scheduler to the debounce() operator.
// Production code
function asyncFunctionToTest(subject) {
subject
.tap((v) => console.log(`Tapping: ${v}`))
.debounce(1000)
.subscribe((v) => {
// Here it would call ReactComponent.setState()
console.log(`onNext: ${v}`)
});
}
The test file would contain the following code to invoke the function and make sure the subject emits the values.
// Testfile
const testScheduler = new Rx.TestScheduler();
const subject = new Rx.Subject();
asyncFunctionToTest(subject);
testScheduler.schedule(200, () => subject.onNext('First'));
testScheduler.schedule(400, () => subject.onNext('Second'))
testScheduler.advanceTo(1000);
The test code above still takes one actual second to do the debounce. The only solution i have found is to pass the TestScheduler into the function and passing it to the debounce(1000, testScheduler) method. This will make the debounce operator use the test scheduler.
My initial idea was to use observeOn or subscribeOn to change the defaultScheduler that is used throughout the operation chain by changing
asyncFunctionToTest(subject);
to be something like asyncFunctionToTest(subject.observeOn(testScheduler)); or asyncFunctionToTest(subject.subscribeOn(testScheduler));
that does not give me the result as i expected, however i presume i might not exactly understand the way the observeOn and subscribeOn operators work. (I guesstimate now that when using these operators it changes the schedulers the whole operation chain is run on, but operators still pick their own schedulers, unless specifically passed?)
The following JSBin contains the runnable example where i passed in the scheduler. http://jsbin.com/kiciwiyowe/1/edit?js,console
No not really, unless you actually patched the RxJS library. I know this was brought up recently as an issue and there may be support for say, being able to change what the DefaultScheduler at some point in the future, but at this time it can't be reliably done.
Is there any reason why you can't include the scheduler? All the operators that accept Schedulers already do so optionally and have sensible defaults so it really costs you nothing given that you production code could simply ignore the parameter.
As a more general aside to why simply adding observeOn or subscribeOn doesn't fix it is that both of those operators really only affect how events are propagated after they have been received by that operator.
For instance you could implement observeOn by doing the following:
Rx.Observable.prototype.observeOn = (scheduler) => {
var source = this;
return Rx.Observable.create((observer) => {
source.subscribe(x =>
{
//Reschedule this for a later propagation
scheduler.schedule(x,
(s, state) => observer.onNext(state));
},
//Errors get forwarded immediately
e => observer.onError(e),
//Delay completion
() => scheduler.schedule(null, () => observer.onCompleted()))
});
};
All the above is doing is rescheduling the incoming events, if operators down stream or upstream have other delays this operator has no effect on them. subscribeOn has a similar behavior except that it reschedules the subscription not the events.

Categories

Resources