React.useEffect runs 4 times instead of once - react-hooks

I am using a "service" to get 4 data items on load. 3 arrays and 1 object
So I use the following:
const { datacol, loaddata, dpcServices ,dpcServicesDetails ,meDetails} = EcgInitialDataSPService();
React.useEffect(() => {
loaddata();
}, []);
I notice, in the debugger, that "loaddata();" runs 4 times instead of once.
React.useEffect should run only once. do I need to add something in the line
}, []);
? I tried :
}, [],[],[],null);
But tslint shows it as an error

Related

Cypress - Loop looking for data and refresh if not found

I need to loop looking for an item in a table and if it's not found, click a refresh button to reload the table. I know I can't use a simple while loop due to the asynchronous nature of cypress. Is there another way to accomplish something like this.
I tried to tweak an example from another post but no luck. Here's my failed attempt.
let arry = []
for (let i = 0; i < 60; i++) { arry.push(i) }
cy.wrap(arry).each(() => {
cy.get('table[class*="MyTableClass"]').then(function($lookForTheItemInTheTable) {
if($lookForTheItemInTheTable.find("MySearchValue")) {
return true
}
else {
cy.get('a[class*="selRefreshTable"]').click()
cy.wait(2000)
}
})
})
Cypress is bundled with lodash. Instead of using a for loop, you can the _.times(). However, I wouldn't recommend this for your situation as you do not know how many times you would like to reiterate.
You'll want to use the cypress-recurse plugin and use it like this example in that repo:
import { recurse } from 'cypress-recurse'
it('gets 7 after 50 iterations or 30 seconds', () => {
recurse(
() => cy.task('randomNumber'), // actions you want to iterate
(n) => n === 7, // until this condition is satisfied
{ // options to pass along
log: true,
limit: 50, // max number of iterations
timeout: 30000, // time limit in ms
delay: 300 // delay before next iteration, ms
},
)
})
Even with the above mentioned, there may be a simplified approach to solving your problem with setting up your app to have the table always display what you are seeking on the first try.

How to map different JSON objects from the fixture into specific spec test file in the cypress

I have the below Input.json as fixture and It contains two different test cases.
Input.json (Fixture folder)
[
{
"searchKeyword":"cypress"
},
{
"username":"QATesting",
"password":"testprofile"
}
]
The above data will validate two different functionality of Google. One is going to validate search engine and another one is going to validate the user login activity (This is just for sample use case which may imitate my actual requirement).
I just created the cypress runner and I just want to run the spec file by using the below runner.js file
const cypress = require('cypress')
const fixtures = require('./cypress/fixtures/Test.json')
const promises = fixtures.map(fixture => {
return cypress.run({
env: {
fixture
},
spec: './cypress/integration/test.spec.js',
});
});
I just added two different It(test cases) respectively in the below "test.spec.js" file. And one test is gonna do the search function and another one is gonna check the existing user login activity:
describe("How to map two different data set with respective test function",() =>{
const baseUrl = "https://www.google.com/";
const testData = Cypress.env('fixture')
beforeEach("",()=>{
cy.visit(baseUrl);
});
it("Test Case1: Search the keyword", function () {
cy.xpath("//input[#name='q']").type(testData.searchKeyword);
cy.xpath("//input[#value='Google Search']").click();
cy.get("//ul/li[2]").should("be.visible");
});
it("Test Case2: login to the gmail account", function(){
cy.xpath("//a[contains(text(),'Sign in')]").click();
cy.xpath("//div[contains(text(),'Use another account')]").click();
cy.xpath("#identifierId").type(testData.username);
cy.xpath("//*[contains(text(),'Next')]").click();
cy.xpath("#password").type(testData.password);
cy.xpath("#submitbtn").click();
})
});
But the second test is getting failed and the testData.username return undefined.
Is there anyway to map the specific JSON array object with specific function in the test.spec.js file?
Not sure how to map the first dataset index with first It (Test case 1) and second dataset index with second test case respectively.
One quick way is to skip if the testData does not have the required properties,
describe("How to map two different data set with respective test function",() =>{
const baseUrl = "https://www.google.com/";
const testData = Cypress.env('fixture')
beforeEach("",()=>{
cy.visit(baseUrl);
});
it("Test Case1: Search the keyword", function () {
if (!testData.searchKeyword) this.skip
cy.xpath("//input[#name='q']").type(testData.searchKeyword);
cy.xpath("//input[#value='Google Search']").click();
cy.get("//ul/li[2]").should("be.visible");
});
it("Test Case2: login to the gmail account", function() {
if (!testData.username) this.skip
cy.xpath("//a[contains(text(),'Sign in')]").click();
cy.xpath("//div[contains(text(),'Use another account')]").click();
cy.xpath("#identifierId").type(testData.username);
cy.xpath("//*[contains(text(),'Next')]").click();
cy.xpath("#password").type(testData.password);
cy.xpath("#submitbtn").click();
})
});
Tagging
You can also get into tags, adding a tag property to the testData
[
{
"tag": "search",
"searchKeyword":"cypress"
},
{
"tag": "user",
"username":"QATesting",
"password":"testprofile"
}
]
Perhaps use a library like cypress-tags, then in the runner script
const cypress = require('cypress')
const fixtures = require('./cypress/fixtures/Test.json')
const promises = fixtures.map(fixture => {
if (fixture.tag) {
process.env.CYPRESS_INCLUDE_TAGS = fixture.tag
}
return cypress.run({
env: {
fixture
},
spec: './cypress/integration/test.spec.js',
});
});
Since your fixtures data is in a array and the username and password fields are at index 1, so in order to access those you have to use:
testData[1].username
testData[1].password
In case if you don't want to use the index value, change the fixture structure to:
{
"searchKeyword": "cypress",
"username": "QATesting",
"password": "testprofile"
}
And in your test directly use:
testData.username
testData.password

Made a query in Watson discovery, tried to duplicate via nodejs sdk and the passages array is empty

After doing a simple natural language query in the build query page, set the options for "include relevant passages to Yes. I get back 5 passages and results. All good. When I try from npm ibm-watson 6 nodejs sdk. I get the results, but an empty passages array with the same natural langauge text.
Here is the the url from the query page showing the options, which I tried all of them
https://api.us-east.discovery.watson.cloud.ibm.com/instances/d45df72b-93e4-4897-b9ac-98dc1e088afc/v1/environments/xx/collections/xx/query?version=2018-12-03&deduplicate=false&highlight=true&passages=true&passages.count=5&natural_language_query=what%20is%20autism
Here is the answer from the query builder page
Here is code example,
var discovery = new watson_discovery_v1({
authenticator : new IamAuthenticator({apikey: msg.startup.discovery_password}),
serviceUrl : msg.startup.discovery_endpoint,
version: '2020-09-22'
});
msg.WDSParams = {
environmentId: "x",
collectionId: "x",
passages: true,
count:5,
natural_language_query: msg.params.input.text
}
discovery.query(msg.WDSParams)
.then(results => {
msg.WDSResults = results; //your query results
node.send(msg);
})
.catch(err => {
console.log('error:', err);
});
Here is the json that came back from the discovery call
I have tried all of the passage options, duplicated the exact options that the query builder used. The same results come back, but no passages. Anyone have an idea? BTW using the Lite plan until I can prove passages works.
The problem was related to the way I called the query method. Below code resolved the issue. This code is for a nodeRed function node.
const watson_discovery_v1 = global.get('watson_discovery_v1');
const { IamAuthenticator } = global.get('ibm_auth');
const discovery = new watson_discovery_v1({
authenticator : new IamAuthenticator({apikey:
msg.startup.discovery_password}),
serviceUrl : msg.startup.discovery_endpoint,
version: '2019-04-30'
});
async function run() {
try {
const result = await discovery.query({
environmentId: 'x',
collectionId: 'x',
passages: true,
passagesCount: 2,
count: 5,
naturalLanguageQuery: msg.params.input.text
})
msg.WDSResults = result
clearTimeout(myTM)
}
catch(e){
node.error(e)
}
node.send(msg);
}
run()

Get All Pages with an Apollo Query

Suppose I have 500 rows in my database. I want to get 100 pages of 50 rows. Here is an example of a fetchMore request.
const fetchNextPage = (props) => {
props.Query.fetchMore({
query: gql(getRows),
variables: {
skip: props.Query.rows.length,
},
updateQuery: (previousResult, next) => {
return {
...previousResult,
rows: [...previousResult.rows, ...next.fetchMoreResult.rows],
};
},
});
}
What I'm unsure about is...
How I can fetch all pages without additional user action?
Since I know the total number of needed pages how can I send them in parallel?
You can actually do that with one operation. Using aliases you can request the same field multiple times with different arguments.
Here is the official explanation about aliases.
In your case it would be something similar to:
query GetAllPages {
page1rows: rows(skip: 0, limit: 50) { # "skip" and "limit" are just regular variable names
#...rowFields
}
page2rows: rows(skip: 50, limit: 50) {
#...rowFields
}
#... etc.
}
In this example page1rows and page2rows are aliases for the rows field. You can choose other aliases.
Note that skip and limit are nothing special, they are plain variables and their use depends on the schema and resolvers on the server. I see in your code you use skip, if you know you'll always get 50 rows then limit is redundant.
The response should be something like:
{
"data": {
"page1rows": [
//... rows
],
"page2rows": [
//... rows
],
//... etc.
}
}
That way works without additional user action, you get all pages at once and there's no need to use fetchMore.

Collection.find() very slow with other BDD Meteor React

I'm working on an app with Meteor and React.
I'm trying to have the content of inventories_array from an external MongoDB database, but it's extremely slow. I wait 7 seconds, I have one object, I wait 5 seconds, two other objects, etc...
Spaces = new Mongo.Collection("Space");
Properties = new Mongo.Collection("Property");
Inventories = new Mongo.Collection("Inventory");
if (Meteor.isClient) {
Meteor.subscribe("Property");
Meteor.subscribe("Space");
Meteor.subscribe("Inventory");
Tracker.autorun(function() {
inventories_array = Inventories.find({quantityBooked: 2},{fields: {priceTaxExcl: 1, property: 1, space: 1}}).fetch();
console.log(inventories_array);
}
if (Meteor.isServer) {
Meteor.publish("Property", function () {
return Properties.find();
});
Meteor.publish("Space", function () {
return Spaces.find();
});
Meteor.publish("Inventory", function () {
return Inventories.find();
});
}
The Inventory Object:
{
...
"property" : ObjectId("..."),
"space" : ObjectId("..."),
"quantityBooked":2,
"priceTaxExcl":...,
...
}
I launch the app with MONGO_URL=mongodb://127.0.0.1:27017/mydb meteor run
Any ideas why it's so slow?
If you look at your network tab in the inspector you'll see all the data flowing from the server to the client for each subscription and you'll be able to judge both how large it is and how long it takes.
I'd recommend at a first step that you alter your Inventory publication as follows:
Meteor.publish("Inventory", function () {
if ( this.userId ){ // only return data if we have a logged-in user
return Inventories.find({quantityBooked: 2},{fields: {priceTaxExcl: 1, property: 1, space: 1}});
} else {
this.ready();
}
});
This way your server is only sending the required fields from the required documents (assuming you only want docs where {quantityBooked: 2}.

Resources