How do you log an objects details to the test console with `cy.log`? - cypress

When you do something like the following:
const someObject = {
key: 1,
anotherKey: 'test'
}
cy.log("Some object", someObject)
It will be rendered in the Cypress UI as Some object, Object{2}
Is there a way to get this to print prettier?
I am on version 10.3.0 here is a result with my actual log message:
Actual code:
const loginInput = {
userContext: "global_",
email: finalEmail,
password,
verificationCode,
};
cy.log("Authenticating via GraphQL with the following details", loginInput);

I ran the same code, and it is showing the key-value pairs in the log.
You can use the Javascript Method Object.entries() to loop over the key-value pair and log them one by one.
const someObject = {
key: 1,
anotherKey: 'test',
anotherKey2: 'test2',
}
Object.entries(someObject).forEach(([key, value]) => {
cy.log(key, value)
})

Related

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

Does not exist on type 'DefaultRootState'. TS2339

I am trying to implement react-redux in login-form input values.
I have added values to the redux state, but I cannot access the data individually from the state object.
Here are the details:
In App.js file
console.log(useSelector((state) => state));
gives result {email: "demo#demo.com" , password: "123456"}
. I am not able to access the email inside the state object using
console.log(useSelector((state) => state.email));
It is giving the error that
'email' does not exist on type 'DefaultRootState'. TS2339
Here is the reducer.js file
let formValues = {
email: "",
password: "",
};
export const inputReducer = (state = formValues, action) => {
switch (action.type) {
case "inputValue":
return { ...state, [action.name]: action.inputValue };
default:
return state;
}
};
Here is the action.txt file
export const handleChange = (name: string, inputValue: string) => {
return {
type: "inputValue",
name: name,
inputValue: inputValue,
};
}
I wrote a function to get rid of this problem :
function getProperty<T, K extends keyof T>(o: T, propertyName: K): T[K] {
return o[propertyName]; // o[propertyName] is of type T[K]
}
You have to pass your object as first parameter, then the name of your property (here it is email or password).
If you want to get all your property at once, you have to encapsulate them in an object property like this:
{ value : {email:"alan.turing#gmail.com",password:"123" } }
i may be late but thought to provide solution. Basically this type of error message appears when you don't provide the typing in the useSelector hook
As per the doc React-Redux which states:
Using configureStore should not need any additional typings. You will,
however, want to extract the RootState type and the Dispatch type so
that they can be referenced as needed.
here in your code block the RootState type is missing, this can be declared in your store file as below
import {createStore} from 'redux';
----------
const store = createStore(rootReducer);
export default store;
export type RootState = ReturnType<typeof store.getState>;
And in your .tsx or .jsx file where exactly you want to access your store values using react-redux hook useSelector add the type as below.
useSelector((state:RootState) => state)

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()

Perform sequential api calls with RxJs?

Is there a way in RxJs to perform two api calls where the second requires data from the first and return a combined result as a stream? What I'm trying to do is call the facebook API to get a list of groups and the cover image in various sizes. Facebook returns something like this:
// call to facebook /1234 to get the group 1234, cover object has an
// image in it, but only one size
{ id: '1234', cover: { id: '9999' } }
// call to facebook /9999 to get the image 9999 with an array
// with multiple sizes, omitted for simplicity
{ images: [ <image1>, <image2>, ... ] }
// desired result:
{ id: '1234', images: [ <image1>, <image2>, ... ] }
So I have this:
var result = undefined;
rxGroup = fbService.observe('/1234');
rxGroup.subscribe(group => {
rxImage = fbService.observe(`/${group.cover.id}`);
rxImage.subscribe(images => {
group.images = y;
result = group;
}
}
I want to create a method that accepts a group id and returns an Observable that will have the combined group + images (result here) in the stream. I know I can create my own observable and call the next() function in there where I set 'result' above, but I'm thinking there has to be an rx-way to do this. select/map lets me transform, but I don't know how to shoe-in the results from another call. when/and/then seems promising, but also doesn't look like it supports something like that. I could map and return an observable, but the caller would then have to do two subscribes.
Looks like flatMap is the way to go (fiddle). It is called like subscribe and gives you a value from a stream. You return an observable from that and it outputs the values from all the created observables (one for for each element in the base stream) into the resulting stream.
var sourceGroup = { // result of calling api /1234
id: '1234',
cover: {
id: '9999'
}
};
var sourceCover = { // result of calling api /9999
id: '9999',
images: [{
src: 'image1x80.png'
}, {
src: 'image1x320.png'
}]
};
var rxGroup = Rx.Observable.just(sourceGroup);
var rxCombined = rxGroup.flatMap(group =>
Rx.Observable.just(sourceCover)
.map(images => ({
id: group.id,
images: images.images
}))
)
rxCombined.subscribe(x =>
console.log(JSON.stringify(x, null, 2)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.min.js"></script>
Result:
{
"id": "1234",
"images": [
{
"src": "image1x80.png"
},
{
"src": "image1x320.png"
}
]
}
You should use concatMap instead of flatMap, it will preserve the order of the source emissions.

GraphQL - Get all fields from nested JSON object

I'm putting a GraphQL wrapper over an exiting REST API as described in Zero to GraphQL in 30 minutes. I've got an API endpoint for a product with one property that points to a nested object:
// API Response
{
entity_id: 1,
nested_object: {
key1: val1,
key2: val2,
...
}
}
Is it possible to define the schema so that I can get this entire nested object without explicitly defining the nested object and all of its properties? I want my query to just specify that I want the nested object, and not need to specify all the properties I want from the nested object:
// What I want
{
product(id: "1") {
entityId
nestedObject
}
}
// What I don't want
{
product(id: "1") {
entityId
nestedObject {
key1
key2
...
}
}
}
I can do the second version, but it requires lots of extra code, including creating a NestedObjectType and specifying all the nested properties. I've also figured out how to automatically get a list of all the keys, like so:
const ProductType = new GraphQLObjectType({
...
fields: () => ({
nestedObject: {
type: new GraphQLList(GraphQLString),
resolve: product => Object.keys(product.nested_object)
}
})
})
I haven't figured out a way to automatically return the entire object, though.
You may try to use scalar JSON type. You can find more here (based on apollographql).
add scalar JSON to a schema definition;
add {JSON: GraphQLJSON} to a resolve functions;
use JSON type in a shema:
scalar JSON
type Query {
getObject: JSON
}
an example of a query:
query {
getObject
}
a result:
{
"data": {
"getObject": {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
}
}
Basic code:
const express = require("express");
const graphqlHTTP = require("express-graphql");
const { buildSchema } = require("graphql");
const GraphQLJSON = require("graphql-type-json");
const schema = buildSchema(`
scalar JSON
type Query {
getObject: JSON
}
`);
const root = {
JSON: GraphQLJSON,
getObject: () => {
return {
key1: "value1",
key2: "value2",
key3: "value3"
};
}
};
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true
})
);
app.listen(4000);
console.log("Running a GraphQL API server at localhost:4000/graphql");
I can do the second version, but it requires lots of extra code, including creating a NestedObjectType and specifying all the nested properties.
Do it! It will be great. That's the way to go in order to use GraphQL to its full potential.
Aside from preventing over-fetching, it also gives you a lot of other benefits like type validation, and more readable and maintainable code since your schema gives a fuller description of your data. You'll thank yourself later for doing the extra work up front.
If for some reason you really don't want to go that route though and fully understand the consequences, you could encode the nested objects as strings using JSON.stringify.
But like I said, I recommend you don't!

Resources