describe("SuiteName", () => {
var numberArray =[1,2,3];
beforeEach(async () => {
//I want 'n' from test in this before each method
console.log("Before Each" + expect.getState().currentTestName + 'number ' + n);
});
test.each(numberArray)("Tesst Name" , async (n) => {
console.log("Current parameter is-> " + n);
});
});
Hi, I am new to Jest and want to understand that how can i get the number value in beforeeach block?
By looking at jest code (the doc didn't help much) it seems the callback passed to beforeEach is called with the done callback argument, which wont help. (source https://github.com/facebook/jest/blob/0e50f7313837bd005a560cb2161423ab06845733/packages/jest-circus/src/run.ts and https://github.com/facebook/jest/blob/66629be6194f5e107a26f406180a6ed597fb3c55/packages/jest-circus/src/utils.ts)
But it doesn't matter as within a describe the beforeEach and the test share the same scope, and within a test suite, tests run sequentially (no overlapping "concurrent" access to testState), so it's perfectly fine to do this:
describe("SuiteName", () => {
const testState = { n: undefined };
var numberArray =[1,2,3];
beforeEach(async () => {
//I want 'n' from test in this before each method
console.log("Before Each" + expect.getState().currentTestName + 'number ' + testState.n);
});
numberArray.forEach(n => {
console.log("Current parameter is-> " + n);
testState.n = n;
test('Tesst Name for n: ' + n, async () => {
console.log("Current parameter is-> " + n);
})
});
});
Problem, you lost some of the benefit of test.each by splitting it into multiple tests sharing the same code. But it seems there's no way around it using test.each.
Alternatively, since the use case of both test.each and beforeEach is preventing duplicating code and making the test more readable, why don't you just chain the beforeEach (async) hook code with the actual test:
describe("SuiteName", () => {
var numberArray =[1,2,3];
const forgetBeforeEachWeAreDoingTestEach = (n) => {
return new Promise((resolve) => {
//I want 'n' from test in this before each method
console.log("Before Each" + expect.getState().currentTestName + 'number ' + n);
resolve();
});
});
test.each(numberArray)('Tesst Name', async (n) => {
forgetBeforeEachWeAreDoingTestEach(n).then(() => {
console.log("Current parameter is-> " + n);
});
});
});
I did the above with a Promise because I'm old school but converting from one to the other is pretty straightforward most of the time
May be just:
await forgetBeforeEachWeAreDoingTestEach(n);
console.log("Current parameter is-> " + n);
Note:
This previous stackoverflow answer here provides a solution similar to the first one (testState) with sinon.sandbox, but does not address the test.each problem (having 1 test with multiple params VS having multiple tests with the same code)
expect.getState() is internal API. Jest doesn't have test-scoped context, it should be handled by a developer.
beforeEach functions are evaluated before respective tests and are unaware of what happens after. Notice that beforeEach are hierarchical and may be applied to tests that don't derive from numberArray.
In this case beforeEach doesn't have benefits as reusable piece of code. Since each function is common for all these tests, it can be:
test.each(numberArray)("Tesst Name" , async (n) => {
console.log("Before Each number ' + n);
console.log("Current parameter is-> " + n);
});
Related
I am using react-select with async create table and implemented this to Netsuite custom page. The problem is that I want to load the getAsyncOptions function to trigger only when the user stops typing. At the moment these line of codes make the input field so slow as the function is getting triggered everytime a letter is added to the input. I also cant insert the fetched data to the state because it will be populated with a thousand records.
Email.tsx
getAsyncOptions(inputValue) {
return new Promise((resolve) => {
var typingTimer; //timer identifier
var doneTypingInterval = 1000; //time in ms (1 second)
if (inputValue.length <= 3) {
return resolve([]);
}
clearTimeout(typingTimer);
if (inputValue) {
return (typingTimer = setTimeout(function () {
var t0 = performance.now();
emailSearch(inputValue).then((data) => {
const returnData = [];
data.forEach((contact) => {
returnData.push({
label: contact.email + " - (" + contact.company + ")",
value: contact.email,
});
});
resolve(returnData);
console.log(
"Call to email search function took " +
(t1 - t0) +
" milliseconds."
);
});
var t1 = performance.now();
}, doneTypingInterval));
}
});
As you see on the above code the it is only delaying the code to trigger. And another problem arises when the user stopped typing for 1 second and continue to type again, it is just delaying the function, and being triggered every 1 second. Here's the rest of the code for your reference.
RenderOpenMode.tsx
<AsyncCreatableSelect
value={props.state.toEmailCollection}
onChange={props.setToEmail}
loadOptions={props.debouncedLoadQuery}
styles={props.customSelectStylesEmail}
components={{
DropdownIndicator: () => null,
IndicatorSeparator: () => null,
}}
isMulti={true}
isSearchable={true}
isValidNewOption={props.isValidNewOption}
placeholder=""
/>
The Functions
this.setToEmail = (toEmailCollection) =>
this.setState({ toEmailCollection: toEmailCollection });
this.setToCc = (toCcCollection) =>
this.setState({ toCcCollection: toCcCollection });
const loadOptions = (inputValue) => this.getAsyncOptions(inputValue));
this.debouncedLoadQuery = debounce(loadOptions, 2000, {
leading: true,
});
Been facing this roadblock for a while and any idea or help would be very much appreciated! Thank you so much and God bless!
Edit:
Added some code. The onChange only updates some state, the problem is the loadOptions as it is the one that is triggering the getAsyncOptions.
I've fixed the issue, it seems that the problem is on the debounce. I am using debounce-promise and the problem is that I've used leading=true option and that makes the UI unresponsive on change.
From this code:
this.debouncedLoadQuery = debounce(loadOptions, 2000, {
leading: true,
});
To this code:
this.debouncedLoadQuery = debounce(loadOptions, 2000);
I am trying to make a poll command for a discord bot in which the user chooses a number of options in the first command (ie '!poll 4') and then chooses the questions and the options. I am having some issues getting the bot to wait for a response before it moves on to the next option in the loop. When I try and use await in the loop it says I cannot use await because it's not an async function, but it is an async function I think. I am very inexperienced with this so I am sure it is a simple error or probably multiple. If anyone can give me advice on a way to make the loop work as intended and ask for each option I would appreciate it. Also is there a way to add if statements to do addFields to an embed? Here is my code:
const Discord = module.require('discord.js');
module.exports = {
name: 'poll',
async execute(message, args) {
function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0) }
if(isNumber(args[1])){
if(args[1]<2) return message.channel.send('Please choose a higher number of options for the poll :)');
if(args[1]>10) return message.channel.send('Please choose a lower number of options for the poll :)');
const filter = response => {
if(!response.author.bot) return response;
};
var question;
var options;
message.channel.send('What question would you like to ask?').then(() => {
message.channel.awaitMessages(filter, { max: 1, time: 15000})
.then(collected => {
question = `${collected.first()}?`;
message.channel.send('Question: ' + question);
for (var i = 0; i < args[1]; i++) {
message.channel.send('What is option ' + (i + 1) + '?').then(() => {
message.channel.awaitMessages(filter, { max: 1, time: 15000})
.then(collected => {
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
})
.catch(collected => {
message.channel.send('Poll has timed out.');
});
})
}
})
.catch(collected => {
message.channel.send('Poll has timed out.');
});
const embed = new Discord.MessageEmbed()
.setColor(3447003)
.setTitle(question)
.setDescription('choose an option')
/*
if (options[0]) .addField('1️⃣:' + option[0])
if (options[1]) .addField('2️⃣:' + option[1])
if (options[2]) .addField('3️⃣:' + option[2])
if (options[3]) .addField('4️⃣:' + option[3])
if (options[4]) .addField('5️⃣:' + option[4])
if (options[5]) .addField('6️⃣:' + option[5])
if (options[6]) .addField('7️⃣:' + option[6])
if (options[7]) .addField('8️⃣:' + option[7])
if (options[8]) .addField('9️⃣:' + option[8])
if (options[9]) .addField('🔟:' + option[9])
*/
message.channel.send(embed).then(embedMessage => {
if (options[0]) embedMessage.react('1️⃣');
if (options[1]) embedMessage.react('2️⃣');
if (options[2]) embedMessage.react('3️⃣');
if (options[3]) embedMessage.react('4️⃣');
if (options[4]) embedMessage.react('5️⃣');
if (options[5]) embedMessage.react('6️⃣');
if (options[6]) embedMessage.react('7️⃣');
if (options[7]) embedMessage.react('8️⃣');
if (options[8]) embedMessage.react('9️⃣');
if (options[9]) embedMessage.react('🔟');
});
});
}
}
}
Since you say you are trying to use await in your loop, let me take the function it is contained in out from your snippet, format it a little, and try to do some explaining. Disclaimer: I am no expert, so I am learning as well.
.then(collected => {
question = `${collected.first()}?`;
message.channel.send(`Question: ${question}`);
for (var i = 0; i < args[1]; i++) {
message.channel.send(
`What is option ${i + 1}?`
).then(() => {
message.channel.awaitMessages(filter, {
"max": 1,
"time": 15000,
}).then(collected => {
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
}).catch(collected => {
message.channel.send("Poll has timed out.");
});
});
}
});
But before that, as the first inner .then() still returns a Promise, you can chain the second inner .then() in the outer scope to avoid nesting them too deep, and leave a single .catch() at the end. On that note, it would probably be more accurate to call the catch's parameter something like error. So here's the new snippet:
.then(collected => {
question = `${collected.first()}?`;
message.channel.send('Question: ' + question);
for (var i = 0; i < args[1]; i++) {
message.channel.send(
`What is option ${i + 1}?`
).then(() => {
message.channel.awaitMessages(filter, {
"max": 1,
"time": 15000,
});
}).then(collected => { // Change .then() chaining
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
}).catch(error => { // Change parameter name
message.channel.send("Poll has timed out.");
});
}
})
What's happening now is that each iteration is running one after the other immediately. You .send() a whole bunch of messages which each return a Promise, and off that Promise, you pass a callback function to .then() which runs once each Promise resolves into a Message. That callback implicitly returns the result of .awaitMessages(), which is also a promise, and once that resolves, the next callback in the next .then() runs with the value of whatever the previous promise resolved to passed in as an argument, and so on.
Alright, so you want to the entire Promise chain to finish processing and resolve before proceeding to the next iteration, right? You can use the await keyword to suspend progress in the relevant anonymous function, until its associated promise-based operation either resolves or rejects. The catch is that that function has to be marked with the async keyword, and in your code, that is not actually the case, you are just making use of Promises and callback functions (regarding "but it is an async function I think"). So, let's add both the aforementioned keywords:
.then(async collected => { // Mark as async
question = `${collected.first()}?`;
message.channel.send('Question: ' + question);
for (var i = 0; i < args[1]; i++) {
await message.channel.send( // Wait for this entire Promise chain to resolve before proceeding
`What is option ${i + 1}?`
).then(() => {
message.channel.awaitMessages(filter, {
"max": 1,
"time": 15000,
});
}).then(collected => {
options[i] = collected.first;
message.channel.send(`Option ${i}: ${options[i]}`);
}).catch(error => {
message.channel.send("Poll has timed out.");
});
}
})
That should cause your desired behaviour, though my edits may have syntax errors as I have not ran it myself. If I got something wrong, please do comment.
You can read more here:
Using async-await
Using Promises
I am using Jest to test a Redux action function fn1. fn1 is a higher order function that wraps fn2. My test is just to make sure fn2 is called when fn1 is executed. Doesn't seems to work. I am thinking about using jest.spyOn, but it doesn't seem to make sense.
myActions.js:
export const fn1 = obj => {
return strInput => {
fn2(strInput, obj);
};
};
export const fn2 = (strInput, obj) => ({name:strInput, obj});
myAction.test.js:
import {fn1, fn2} from myAction.test.js
it("should call fn2", () => {
fn1({test:"test"})("David")
expect(fn2).toHaveBeenCalled();
});
In a way, I feel what you're trying to do is testing an implementation detail and not the API of the function, but in unit testing, you really want to be testing, or asserting, the correct output based upon a specified input, i.e. f(x) = y, testing input x yields output y.
Presumably your fn2 will have its own unit test, so you can assume it is tested and correct when it's used in other functions, like fn1.
Here's how I would test fn1:
it("should compute composed value", () => {
expect(fn1({ test: "test" })("David")).toEqual({
name: "David",
obj: { test: "test" }
});
});
I would say the typical use-case for spying on, or asserting, a function call is the case of callbacks. Callbacks aren't part of the implementation of a function, but usually an external side-effect.
const fn3 = (value, callback) => {
// bunch of code logic
callback(value);
// more code logic
return true;
};
it("should callback function", () => {
const cb = jest.fn();
fn3(3, cb);
expect(cb).toHaveBeenCalledWith(3);
});
Not really part of the answer but informative
At this point I want to point out an error in nomenclature: Higher Order Function vs Currying
A Higher Order Function is one that takes as input a function and returns a new function. Examples include .map, .filter, functional composition
const plus3 = x => x + 3; // a first order function
const double = (fn, v) => fn(v) * 2; // a higher order function
const plus3AndDouble = x => double(plus3, x); // a (decorated) first order function
console.log(plus3AndDouble(0)); // (0 + 3) * 2 = 6
console.log(plus3AndDouble(1)); // (1 + 3) * 2 = 8
What you've accomplished is actually a concept called currying wherein you take a function that takes multiple inputs and convert it to a sequence of functions each taking a single input.
const foo = (a, b, c) => a + b * c;
const bar = a => b => c => foo(a, b, c);
console.log(foo(1, 2, 3) === bar(1)(2)(3)); // true
I have an array of values to delete but want the user to confirm that he wants to delete the value. I plan to use the prompt Node module but don't know how to combine them in such a way that I move to the next value in the array after the user has confirmed (or not) the deletion of the current value. In short, the following code won't work:
var arr = ["1","2","3"];
for (var i in arr) {
console.log("Delete " + arr[i] + "?");
prompt.get(['response'], function (err, result) {
console.log(' reponse: ' + result.response);
// if ...
});
}
Any idea? Many thanks.
#user1280859 is right about the issue but maybe it is better to take adventage of prompt features instead of recursion or asyncronous loop:
var prompt = require('prompt');
prompt.start();
var arr = ["1","2","3"];
var promptArr = arr.map(function(num) {
return "Delete " + num + "?";
});
prompt.get(promptArr, function (err, result) {
console.log(result);
});
OutPut:
prompt: Delete 1?: y
prompt: Delete 2?: n
prompt: Delete 3?: y
{ 'Delete 1?': 'y', 'Delete 2?': 'n', 'Delete 3?': 'y' }
Your code is not working because you're trying to use async calls prompt.get within syncronous loop for.
In case you have to make async calls you should to wait its callbacks. Particulary in your case you can use something like
function removeEl(idx, arr, done){
if(idx>=arr.length) return done(arr);
prompt.get(['response'], function (err, result) {
console.log(' reponse: ' + result.response);
if(/*user said yes*/){
arr.splice(idx, 1);
removeEl(idx, arr, done)
}else{
removeEl(idx+1,arr, done)
}
});
}
function removeWithPrompt(arr, done){
removeEl(0, arr, done);
}
removeWithPrompt([1,2,3], function (modified) {
// modified contains non-deleted elements
})
Or use asyncronous loops like async.each
Let's say you have an Angular app that's showing a list of places. There is a button to get your current location, and clicking the button orders the list according to distance from your location, nearest first.
To test this in Protractor you want to be able to click the button and inspect the list:
it('Should order items according to distance', function () {
locButton.click();
expect(...).toBe(...); // Check that the first item on the list
// the closest to the given lat/long
});
Now, let's say the button calls a method in a controller, the controller calls a method in a service, and the service calls navigator.geolocation.getCurrentPosition() (and, for good measure, that call is wrapped in a promise). The best way to test this is to mock the call to getCurrentPosition() and return a specific latitude and longitude, so that has the required effects all the way back up the chain to the page output. How do you set that up that mock?
I tried the method in this answer to a similar question about Jasmine, creating a spy on navigator.geolocation, with the result:
ReferenceError: navigator is not defined
I also tried mocking the service with something similar to this answer with the result:
ReferenceError: angular is not defined
Update:
Found a solution so I answered my own question below, but I'm really, really hoping there's a better answer than this.
Found a way to do it by using browser.executeScript() to run some JavaScript directly in the browser. For example:
describe('Testing geolocation', function () {
beforeEach(function () {
browser.executeScript('\
window.navigator.geolocation.getCurrentPosition = \
function(success){ \
var position = { \
"coords" : { \
"latitude": "37",
"longitude": "-115" \
} \
}; \
success(position); \
}')
});
it('Should order items according to distance', function () {
locButton.click();
expect(...).toBe(...); // Check that the first item on the list
// the closest to the given lat/long
});
});
This works, but it's ugly. I did my best to make the string passed to browser.executeScript() as readable as possible.
EDIT
Here's a cleaned up version with two functions to mock successes and errors:
describe('Geolocation', function () {
function mockGeo(lat, lon) {
return 'window.navigator.geolocation.getCurrentPosition = ' +
' function (success, error) {' +
' var position = {' +
' "coords" : {' +
' "latitude": "' + lat + '",' +
' "longitude": "' + lon + '"' +
' }' +
' };' +
' success(position);' +
' }';
}
function mockGeoError(code) {
return 'window.navigator.geolocation.getCurrentPosition = ' +
' function (success, error) {' +
' var err = {' +
' code: ' + code + ',' +
' PERMISSION_DENIED: 1,' +
' POSITION_UNAVAILABLE: 2,' +
' TIMEOUT: 3' +
' };' +
' error(err);' +
' }';
}
it('should succeed', function () {
browser.executeScript(mockGeo(36.149674, -86.813347));
// rest of your test...
});
it('should fail', function () {
browser.executeScript(mockGeoError(1));
// rest of your test...
});
});
Protractor tests are e2e, so you really do not have access to your backend code and results.
I had a similar issue in that I wanted to see my "post" output when I click submit in a form.
Created this that populates a test results in the dom, so you can see such backend stuff like this.
Not the best, but do not see another way to do this.
/////////////////////////////////////////////////////////////////
//markup added for testing
<div ng-controller="myTestDevCtrl">
<button id="get-output" ng-click="getOutput()">get output</button>
<input ng-model="output" />
</div>
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//test controller to show ajax data coming out
myTestModule.controller('myTestDevCtrl', function($scope,dataProxy) {
$scope.getOutput = function() {
$scope.output = dataProxy.getData();
}
})
//small service to capture ajax data
.service('dataProxy',function() {
var data;
return {
setData : function(_data) {
data = decodeURIComponent(_data);
},
getData : function() {
return data;
}
}
})
.run(function($httpBackend,dataProxy) {
//the office information post or 'save'
$httpBackend.when('POST',/\/api\/offices/)
.respond(function (requestMethod, requestUrl, data, headers) {
//capture data being sent
dataProxy.setData(data);
//just return success code
return [ 200, {}, {} ];
});
});
//make myTestModule require ngMockE2E, as well as original modules
angular.module('myTestModule').requires = [
'ngMockE2E'
];
/////////////////////////////////////////////////////////////////