I have the following method in a utils.js
/**
* To verify the any array of objevt with given keyword
* #param {*} list
* #param {*} searchKeyword
* #returns true if any mismatch found and vice versa
*/
export const verifyMatchedItems = (list, searchKeyword) => {
var matchedItems = {
title: [],
region: [],
country: [],
};
var nonMatchedItems = [];
list.map((item, index) => {
if (item.title.includes(searchKeyword)) {
matchedItems.title.push(item.title);
} else if (item.region.includes(searchKeyword)) {
matchedItems.region.push(item.region);
} else if (item.country.includes(searchKeyword)) {
matchedItems.country.push(item.country);
} else {
nonMatchedItems.push(item);
}
});
cy.log("Matched-Items:" + JSON.stringify(matchedItems));
if (nonMatchedItems.length>0) {
cy.log("nonMatchedItems:" + JSON.stringify(nonMatchedItems));
}
return nonMatchedItems.length>0;
};
And I am using it in test like this,
//fetch all data in Search Results page and store it
cy.getResultList().then((searchResultDetails) => {
//verify the keyword is matched with result attributes
var flag = Utils.verifyMatchedItems(searchResultDetails, searchKeyword);
expect(flag).to.be.equal(false);
In case, the flag value is true then I want the test to fail. But when I try, it is failing but not executing any previous steps like cy.log(). How can we possibly make cypress to throw an error / fail a test with a customised message saying Failed to due bla bla bla...
You can use throw new error and force a failure with custom message.
throw new Error('The condition was not met!')
In case anyone want this, try to wrap the expect() in a cy.then().
This is because the previous function contains some Cypress commands (only cy.log() in this case).
To make the final check after those commands, put it on the Cypress queue.
//fetch all data in Search Results page and store it
cy.getResultList().then((searchResultDetails) => {
//verify the keyword is matched with result attributes
var flag = Utils.verifyMatchedItems(searchResultDetails, searchKeyword);
cy.then(() => expect(flag).to.be.equal(false))
})
Related
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)
})
Im using postgraphile and i have this query:
query Products($categories: [String]){
products(
filter: {
category: { in: $categories }
}) {
id
name
category
}
}
is there a way to not use the filter if the $categories array is empty ?
In case the array is not set or it's empty, i want to get all the results.
i saw there is an option to pass filter as an argument, but I wonder if there is another support.
Without making the filter a generic argument on the client side, There are two server-side options for filter operator customization in the postgraphile-plugin-connection-filter (v2.3.0) plugin. Both require you to create an additional plugin that registers a build hook.
Option 1: Use addConnectionFilterOperator
addConnectionFilterOperator is a build extension function added by ConnectionFilterPlugin that is meant to allow you to add a new operators for custom functionality.
Option 2: Modify Connection Filter Operators Directly
It's also possible to directly change the SQL output of a given operator by modifying the OperatorSpec values for each GraphQLType. These specs can be found in the following fields on the Build object:
connectionFilterArrayOperators,
connectionFilterEnumOperators,
connectionFilterRangeOperators,
connectionFilterScalarOperators
Below is an example of a combined Plugin implementation for Options 1 and 2 that omit SQL generation when the argument value array for the operator is empty - effectively nullifying the operator and resulting in full data returns. This implementation allows for easy switching between modifying the in operators directly and adding new expansiveIn operators to each connection filter's scalar fields.
import type { Build } from "postgraphile";
import type { Plugin, Options } from "graphile-build";
import type { AddConnectionFilterOperator } from "postgraphile-plugin-connection-filter/dist/PgConnectionArgFilterPlugin";
import type { PgType, SQL } from "graphile-build-pg";
import type { GraphQLInputType } from "graphql";
import type { OperatorSpec } from "postgraphile-plugin-connection-filter/dist/PgConnectionArgFilterOperatorsPlugin";
export interface InOperatorEmptyArrayCustomizationPluginBuildOpts {
inOperatorEmptyArrayCustomizationPlugin?: {
/**
* Add new "expansiveIn" operators with custom empty array instead of
* modifying existing "in" operators
*/
addNewOperator?: boolean;
};
}
/**
* Implements custom empty array handling either by registering a new "expansiveIn"
* operator for each connection filter or by modifying the existing "operators".
* This plugin must be appended AFTER postgraphile-plugin-connection-filter
* as it depends on its build extensions.
*/
const InOperatorEmptyArrayCustomizationPlugin: Plugin = (builder, options) => {
const { inOperatorEmptyArrayCustomizationPlugin } = options as Options &
InOperatorEmptyArrayCustomizationPluginBuildOpts;
const addNewOperator =
inOperatorEmptyArrayCustomizationPlugin?.addNewOperator === true;
// create a build hook to access ConnectionFilterPlugin build extensions.
builder.hook("build", (build) => {
const {
pgSql: sql,
graphql: { GraphQLList, GraphQLNonNull },
// this function is added as a build extension by the ConnectionFilterPlugin
// and allows for the addition of custom filter operators.
addConnectionFilterOperator,
gql2pg,
// this contains all existing ConnectionFilterPlugin scalar operators
// by GraphQL type
connectionFilterScalarOperators,
} = build as Build & {
addConnectionFilterOperator: AddConnectionFilterOperator;
connectionFilterScalarOperators: Record<
string,
Record<string, OperatorSpec>
>;
};
// Generate "in" SQL fragment from argument input values if values are
// present in the array. Otherwise, return null.
const resolveListSqlValue = (
input: unknown,
pgType: PgType,
pgTypeModifier: number | null,
resolveListItemSqlValue: (
elem: unknown,
pgType: PgType,
pgTypeModifier: number | null
) => unknown
) =>
(input as unknown[]).length === 0
? null
: sql.query`(${sql.join(
(input as unknown[]).map((i) =>
resolveListItemSqlValue
? resolveListItemSqlValue(i, pgType, pgTypeModifier)
: gql2pg(i, pgType, pgTypeModifier)
),
","
)})`;
// checks whether value is present before adding the sql filter fragment.
const resolve = (i: SQL, v: SQL) =>
v != null ? sql.fragment`${i} IN ${v}` : null;
// Find all the scalar GraphQLTypes that have an "in" operator.
const typesWithScalarInOperators = Object.entries(
connectionFilterScalarOperators
)
.filter(([, operations]) => operations.in)
.map(([typeName]) => typeName);
// modify existing "in" operators for every scalar type.
if (!addNewOperator) {
// The graphile build engine will emit a warning if you create
// a new build object using the standard javascript mechanisms.
// It will also throw an error if the existing
// connectionFilterScalarOperations field is replaced in the extension
// object...
const extendedBuild = build.extend(build, {});
// ...so we merge in the new operators in a separate step.
typesWithScalarInOperators.forEach((typeName) => {
extendedBuild.connectionFilterScalarOperators[typeName].in = {
// see https://github.com/graphile-contrib/postgraphile-plugin-connection-filter/blob/v2.3.0/src/PgConnectionArgFilterOperatorsPlugin.ts#L80-L85
// for existing "in" operator configuration
...extendedBuild.connectionFilterScalarOperators[typeName].in,
resolveSqlValue: resolveListSqlValue,
resolve,
};
});
return extendedBuild;
}
// Otherwise add a new operator called "inExpansive" that implements the custom
// empty array argument handling.
// see https://github.com/graphile-contrib/postgraphile-plugin-connection-filter/blob/v2.3.0/__tests__/customOperatorsPlugin.ts
// for `addConnectionFilterOperator` usage examples.
addConnectionFilterOperator(
// add the new operator to any type that has an "in" operator.
typesWithScalarInOperators,
"inExpansive",
"Included in the specified list -unless list is empty in which case this operator is not applied.",
// list of non-null element type
(fieldInputType: GraphQLInputType) =>
new GraphQLList(new GraphQLNonNull(fieldInputType)),
resolve,
{
resolveSqlValue: resolveListSqlValue,
}
);
return build;
});
};
export default InOperatorEmptyArrayCustomizationPlugin;
Append plugin after ConnectionFilterPlugin in the Postgraphile middleware options:
// ...
appendPlugins: [
// ..
ConnectionFilterPlugin,
AddInExpansiveFilterOperatorPlugin
],
// ...
To enable the expansiveIn operator (Option 1) add the relevant configuration to `graphileBuildOptions in the Postgraphile middleware options:
graphileBuildOptions: {
inOperatorEmptyArrayCustomizationPlugin: {
addNewOperator: true
},
// other plugin options
}
You can use inExpansive operator the same way as the in operator:
query Products($categories: [String]){
products(
filter: {
category: { inExpansive: $categories }
}) {
id
name
category
}
}
let expectedKey = 'Student';
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
if(expectedKey === 'Student'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.studentCode);
}
if(expectedDKey === 'Department'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.departmentCode);
}
if(expectedKey === 'Paper'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.paperCode);
}
if(expectedKey === 'Results'){
cy.get('app-screen').find('#code-details').should('have.text', appDetails.resultsCode);
}
}
I don't want to use these many if blocks as there will more keys in the future. Instead, I have to pick the required value for studentCode, departmentCode, paperCode, or resultsCode from JSON based on expectedKey. Any help please?
You can access object properties by dot notation (foo.bar) or bracket notation (foo['bar']). In your case, you'll have to ensure expectedKey matches a valid key in your object with assertion before the cy commands.
let expectedKey = 'studentCode';
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
expect(appDetails, 'valid key').to.have.property(expectedKey)
cy.get('app-screen').find('#code-details').should('have.text', appDetails[expectedKey]);
}
Assuming that you have the expectedKey inside the cy.readFile(), you can do like this:
Create a custom command at cypress/support/commands.js:
Cypress.Commands.add('codeDetailsText', (expectedKey, appDetails) => {
expectedKeyCode = expectedKey.toLowerCase() + 'Code'
cy.get('app-screen')
.find('#code-details')
.should('have.text', appDetails[expectedKeyCode])
})
In your test just write:
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
//Assuming expectedKey value is available here
cy.codeDetailsText(expectedKey, appDetails)
})
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
I am doing a chat app using parse server, everything is great but i tried make to list just last message for every remote peer. i didn't find any query limitation how to get just one message from every remote peer how can i make this ?
Query limitation with Parse SDK
To limit the number of object that you get from a query you use limit
Here is a little example:
const Messages = Parse.Object.extend("Messages");
const query = new Parse.Query(Messages);
query.descending("createdAt");
query.limit(1); // Get only one result
Get the first object of a query with Parse SDK
In you case as you really want only one result you can use Query.first.
Like Query.find the method Query.first make a query and will return only the first result of the Query
Here is an example:
const Messages = Parse.Object.extend("Messages");
const query = new Parse.Query(Messages);
query.descending("createdAt");
const message = await query.first();
I hope my answer help you 😊
If you want to do this using a single query, you will have to use aggregate:
https://docs.parseplatform.org/js/guide/#aggregate
Try something like this:
var query = new Parse.Query("Messages");
var pipeline = [
{ match: { local: '_User$' + userID } },
{ sort: { createdAt: 1 } },
{ group: { remote: '$remote', lastMessage: { $last: '$body' } } },
];
query.aggregate(pipeline)
.then(function(results) {
// results contains unique score values
})
.catch(function(error) {
// There was an error.
});