How can I use a string representation of an enum in a Jest test.each case? - enums

When writing a Jest test.each() test using an enum like below
enum MyEnum {
FirstValue,
SecondValue,
ThirdValue
}
describe('my example', () => {
test.each([MyEnum.FirstValue, MyEnum.SecondValue, MyEnum.ThirdValue])(
'%o',
(input: MyEnum) => {
expect(input).toBeDefined();
}
);
});
The output text is:
my example
0
1
2
I would like it to be
my example
FirstValue
SecondValue
ThirdValue
Using other formatting parameters like %p or %j like listed in the Jest docs don't change anything

I just thought of an answer, I could use describe.each in stead:
describe.each(testcases)('my test', (input: MyEnum) => {
test(`${MyEnum[input]}`, () => {
expect(input).toBeDefined();
});
});

Related

How to return result from Cypress function after set

I have the following code:
function getValues(date)
{
let totalValue=-10;
cy.visit('http://localhost/page1');
cy.get(".total").then((span)=>
{
console.log("Values: "+span.text())
totalValue=span.text()
}
)
//.then(()=> totalValue);
return totalValue;
}
I would like to get the value of the element so I use this:
it('Testing values',()=>
{
var totalValues=getValues(startDate)
console.log("Total values: "+ totalValues)
}
Unfortunately, it never waits for the variable to be set.
Either I get this -10 (when I use the return) or undefined (when I use then) as results and the order is incorrect:
Total values: -10
Values: 2.5
How could I force the execution to wait until the correct value is set?
With a plain JS function, return the last internal command and use .then() on the return value.
function getValues(date) {
cy.visit('http://localhost/page1');
return cy.get(".total").then((span) => span.text())
}
it('Testing values', () => {
getValues(startDate).then(totalValues => {
console.log("Total values: "+ totalValues)
})
})
Or create a custom command
Cypress.Commands.add('getValues', (date) => {
cy.visit('http://localhost/page1');
cy.get(".total").then((span) => span.text()) // does not need an explicit return
})
it('Testing values', () => {
cy.getValues(startDate).then(totalValues => {
console.log("Total values: "+ totalValues)
})
})
If you want var totalValues = ... in the test, there's no way to do it. You always need a .then() because the value is obtained asynchronously.
You can use an alias to store the value, but as already mentioned you have to access with .then() in the test
function getValues(date) {
let totalValue = -10;
cy.visit('http://localhost/page1');
cy.get(".total")
.then((span) => {
totalValue = span.text()
cy.wrap(totalValue).as('totalValues') // set alias value,
// must be inside .then()
})
}
it('Testing values', () => {
getValues(startDate)
cy.get('#totalValues').then(totalValues => {
console.log("Total values: "+ totalValues)
})
})
Exception to using .then()
You can use before() block in conjunction with alias to avoid using .then(), but it's a specific pattern.
function getValues(date) {
let totalValue = -10;
cy.visit('http://localhost/page1');
cy.get(".total")
.then((span) => {
totalValue = span.text()
// will put totalValue on the "this" scope
cy.wrap(totalValue).as('totalValues')
})
}
before(() => {
getValues(startDate) // calling in before hook resolves all asynchronous calls
})
it('Testing values', function() { // must use a function() format
// your value is a property of "this"
console.log("Total values: " + this.totalValues)
})
The mixture of async(cypress) and sync code is the reason for the wrong order of logging.
In the scope of what you shared, here is what you can use to have the logging in the correct order. I'm not sure where the startDate or where else you plan on using or the html you'll use the getValues() function.
Here is the spec file.
<div class="total">2.5</div>
function getValues(date) {
let totalValue = -10
return cy
.get(".total")
.invoke("text")
.then((value) => cy.log("Value: " + value))
}
getValues().then((totalValues) => {
cy.log("Total values: " + totalValues)
})

How to get the return value from a custom command in Cypress?

I am writing a long test so I added the most reusable part to a Command Folder, however, I need access to a certain return value. How would I get the return value from the command?
Instead of directly returning the salesContractNumber, wrap it and then return it like this:
Your custom command:
Cypress.Commands.add('addStandardGrainSalesContract', () => {
//Rest of the code
return cy.wrap(salesContractNumber)
})
In your test you can do this:
cy.addStandardGrainSalesContract().then((salesContractNumber) => {
cy.get(FixingsAddPageSelectors.ContractNumberField).type(salesContractNumber)
})
Generally speaking, you need to return the value from the last .then().
Cypress puts the results of the commands onto the queue for you, and trailing .then() sections can modify the results.
Cypress.Commands.add('addStandardGrainSalesContract', () => {
let salesContractNumber;
cy.get('SalesContractsAddSelectors.SalesContractNumber').should($h2 => {
...
salesContractNumber = ...
})
.then(() => {
...
return salesContractNumber
})
})
cy.addStandardGrainSalesContract().then(salesContractumber => {
...
Or this should work also
Cypress.Commands.add('addStandardGrainSalesContract', () => {
cy.get('SalesContractsAddSelectors.SalesContractNumber').should($h2 => {
...
const salesContractNumber = ...
return salesContractNumber; // pass into .then()
})
.then(salesContractNumber => {
...
return salesContractNumber // returns to outer code
})
})
cy.addStandardGrainSalesContract().then(salesContractumber => {
...
Extra notes:
const salesContractHeader = $h2.text() // don't need Cypress.$()
const salesContractNumber = salesContractHeader.split(' ').pop() // take last item in array

How to use getText inside a div and store it in a varaible in Cypress

cy.xpath('some xpath').then(($btn)=>{const text = $btn.text()})
I am currently using like this but i can use the text only inside .then() so i have problem of calling it outside .
Please tell me how to get the text and store in a variable.
e.g. var text= cy.xpath('some xpath') like this so that i can use it anywhere in inside the code.
You can use an alias using as (cypress docs) to store your innerText.
cy.xpath('some xpath').invoke('text').as('text')
Now later if you want to use it you can use it like:
cy.get('#text').then((text) => {
//Do something with text
})
Please be aware that all alias are cleared between tests.
This means if you do this
it('checks some xpath', () => {
cy.xpath('some xpath')
.then(($btn) => { return $btn.text()})
.as('myvar')
})
it('uses myvar', () => {
cy.get('#myvar').then((myvar) => {
const newvar = myvar + '2' // FAILS - "alias myvar has not been declared
})
})
Some better ways
Use alias from this scope
it('checks some xpath', () => {
cy.xpath('some xpath')
.then(($btn) => { return $btn.text()})
.as('myvar')
})
it('uses myvar', function() { // note "function"
const newvar = this.myvar + '2' // this.myvar is set by alias but NOT cleared
})
Use "back-flip" variable
let myvar;
it('checks some xpath', () => {
cy.xpath('some xpath')
.then(($btn) => { return $btn.text()})
})
it('uses myvar', () => { // arrow function is ok
const newvar = myvar + '2' // myvar is global to both tests
})

TypeError: You provided an invalid object where a stream was expected

The following code works. It does an ajax request and then call 2 actions, on at a time:
export const loadThingsEpic = action$ => {
return action$.ofType(LOAD_THINGS)
.mergeMap(({things}) => {
const requestURL = `${AppConfig.serverUrl()}/data/things`;
return ajax.getJSON(requestURL)).map(response => {
return finishLoadingThings(response);
}).map(() => {
return sendNotification('success');
});
})
.catch(e => {
return concat(of(finishLoadingThings({ things: {} })),
of(sendNotification('error')));
});
}}
But this code does not:
export const loadThingsEpic = action$ => {
return action$.ofType(LOAD_THINGS)
.mergeMap(({things}) => {
const requestURL = `${AppConfig.serverUrl()}/data/things`;
return ajax.getJSON(requestURL).switchMap(response => {
return concat(of(finishLoadingThings(response)),
of(sendNotification('success')));
});
})
.catch(e => {
return concat(of(finishLoadingThings({ things: {} })),
of(sendNotification('error')));
});
}
I've replace the map by a switchMap to merge 2 actions together (as seen in many other post). It works in the catch if an exception is thrown. I'm wondering whats wrong with the code. I'm guessing it's because I can't seem to really grasp when to use: map, swicthMap and mergeMap.
sendNotification and finishLoadingthings returns action object:
export function finishLoadingThings(data: any) {
return {
type: FINISH_LOADING_THINGS,
data,
};
}
Thanks!
The code provided as-is appears to work as intended: https://jsbin.com/becapin/edit?js,console I do not receive a "invalid object where stream expected" error when the ajax succeeds or fails.
Are you sure the error is coming from this code?
On a separate note, you might be happy to hear that Observable.of supports an arbitrary number of arguments, each one will be emitted after the other. So instead of this:
.switchMap(response => {
return concat(of(finishLoadingThings(response)),
of(sendNotification('success')));
});
You can just do this:
.switchMap(response => {
return of(
finishLoadingThings(response),
sendNotification('success')
);
});
This would not have caused a bug though, it's just cleaner.
I manage to fix my problem, by doing the switchMap at the same level than the mergeMap. Like this:
export const loadThingsEpic = action$ => {
return action$.ofType(LOAD_THINGS)
.mergeMap(({things}) => {
const requestURL = `${AppConfig.serverUrl()}/data/things`;
return ajax.getJSON(requestURL).switchMap(response => {
return of(response);
});
})
.switchMap((res) => {
return concat(of(finishLoadingThings(res.value)),
of(sendNotification('success')));
})
.catch(e => {
return concat(of(finishLoadingThings({ things: {} })),
of(sendNotification('error')));
});
}
Don't quite get it yet.

RxJS combineLatest on object of Observables?

I've found myself with a mixed object of values and observables. Something like:
const mixedObject = {
field1: "Hello",
field2: Rx.Observable.of('World'),
field3: Rx.Observable.interval(1000),
};
What would be the best way of doing something like a combineLatest on this object to get a stream of plain objects.
For example, I'd like to do something like:
Rx.Observable.combineLatest(mixedObject).subscribe(plainObject => {
console.log(plainObject.field1, plainObject.field2, plainObject.field3);
// Outputs:
// > Hello World 0
// > Hello World 1
// > Hello World 2
});
You didn't specify if field1 is always a value while field2 and field3 are observables. I will try to give a more general solution.
function mixedToSync(obj: any): Observable<any> {
return Observable.combineLatest(
Object.keys(obj).map((key: string) => {
let value = obj[key];
if(Observable.prototype.isPrototypeOf(value)){
return value.map(v => ({[key]: v}));
} else {
return Observable.of({[key]: value});
}
}))
.map(propsObservables => Object.assign({}, propsObservables));
}
and then you can use it on your object:
mixedToSync(mixedObject).subscribe(v => console.log(v));
Or to match your example:
mixedToSync(mixedObject).subscribe(v => console.log(v.field1, v.field2, v.field3));
This is now in RXJS 7.
const res = combineLatest({ foo: a$, bar: b$, baz: c$ });
res.subscribe(({ foo, bar, baz }) => { console.log(foo, bar, baz); });

Resources