Is there a way to set {matchCase:false} globally when using contains? - cypress

So i'm using cypress and most of my selector is using contains
So instead of using {matchCase:false} in every contains is there a way to set it globally?

There's no global configuration setting for what you need, but it's a good idea and I recommend you raise it with Cypress as an enhancement. You might get it done as part of the Query API enhancement.
In the meantime, if you want something now you can take a composition approach.
Create a case-insensitive version of cy.contains() based on the built-in one.
Cypress.Commands.add('contains_i', {prevSubject: 'optional'},
(subject, filter, text, options = {}) => {
if (typeof text === 'object') {
options = text
text = filter
filter = undefined
}
if (!options.matchCase) {
// allow true to be passed but default to false
options.matchCase = false
}
return cy.contains(subject, filter, text, options)
})
Then you can globally replace in VS Code using Ctrl-Shift-H
which will do both parent cy.contains() and child cy.get().contains() instances.
You can preview and remove certain change instances.

Jennifer Shehane (an Engineering Manager at Cypress) recommends overriding the .contains() command:
Cypress.Commands.overwrite('contains', (originalFn, subject, filter, text, options = {}) => {
// determine if a filter argument was passed
if (typeof text === 'object') {
options = text
text = filter
filter = undefined
}
options.matchCase = false
return originalFn(subject, filter, text, options)
})
Additionally, you could use a case-insensitive regex matcher instead of just a string, although you would need to do this in every instance:
cy.contains(/MyStRiNg/i);
// matches "mystring", "MYSTRING", and any combination of uppercase/lowercase inbetween

Related

How to override 'data-testid' in the 'findByTestId function from Cypress Testing Library

Most of my existing codebase uses a 'id' only in few places 'data-testId' attribute present.
tried this code
import { configure } from '#testing-library/cypress';
configure({ testIdAttribute: ['data-testId','id'] });
But, still its not working.
Is there any way to use 'id' value in any of the testing-library functions.
My HTML code is something like:
<div class="some random class name" id="userprofile-open" role="button">SB</div>
I want click that element with this code:
cy.findByTestId("userprofile-open", { timeout: 120000 }).click();
I don't think you can configure testing-library with an array of ids, ref API configuration,
import { configure } from '#testing-library/cypress'
configure({ testIdAttribute: 'id' })
But even this fails. Instead you have to use the Cypress command to change the attribute name (only one name is allowed).
cy.configureCypressTestingLibrary({ testIdAttribute: 'id' })
To use either/or attribute name you can change the attribute name on the fly, wrapping it in a custom command (based on Custom Queries)
Cypress.Commands.add('findByTestIdOrId', (idToFind) => {
let result;
const { queryHelpers } = require('#testing-library/dom');
let queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'data-testId');
result = queryAllByTestId(Cypress.$('body')[0], idToFind)
if (result.length) return result;
queryAllByTestId = queryHelpers.queryAllByAttribute.bind(null, 'id');
result = queryAllByTestId(Cypress.$('body')[0], idToFind);
if (result.length) return result;
throw `Unable to find an element by: [data-test-id="${idToFind}"] or [id="${idToFind}"]`
})
cy.findByTestIdOrId('my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
Note this custom command works only for synchronous DOM.
If you need to have Cypress retry and search for either/or attribute, don't use testing-library in the custom command.
Instead use Cypress .should() to enable retry
Cypress.Commands.add('findByTestIdOrId', (selector, idToFind) => {
cy.get(selector)
.should('satisfy', $els => {
const attrs = [...$els].reduce((acc, el) => {
const id = el.id || el.getAttribute('data-test-id') // either/or attribute
if (id) {
acc.push(id)
}
return acc
}, [])
return attrs.some(attr => attr === idToFind); // retries when false
})
.first(); // may be more than one
})
cy.findByTestIdOrId('div', 'my-id')
.should('have.attr', 'id', 'my-id')
// passes and logs "expected <div#my-id> to have attribute id with the value my-id"
The usual cypress way - which has an inherent check on the element visibility and existence as well as included retries for a period of time is using cy.get()
If you want to select element using property like data-id you need this sintax: cy.get('[propertyName="propertyValue"]')
If you want select an element by CSS selector you just pass CSS selector like this:
cy.get('#id')

Schema Directives don't execute in LTR order specified in schema

I am using graphql-tools#v6 and I have implemented two directives #map and #filter. My goal is to use them like a map and filter pipeline. In some cases, I want to map before filtering and in other cases, vice-versa. The directives are implemented using the Schema Directives API and they work as expected when only one directive is applied.
However, if I use them together, then they always execute in one specific order which doesn't match how they are declared in the schema.
For example
directive #map on FIELD_DEFINITION
directive #filter on FIELD_DEFINITION
# usage
type MyType {
list1: [String!]! #map #filter
list2: [String!]! #filter #map
}
In this case, either both fields are mapped and then filtered or vice-versa. The order is controlled by how I pass them in schemaTransforms property.
const schema = makeExecutableSchema({
schemaTransforms: [mapDirective, filterDirective] # vs [filterDirective, mapDirective]
});
I believe since these transforms are passed as an array, so their order of execution depends on the ordering of array. I can replace them with directiveResolvers but they are limited in what they can do.
But what throws me off is the following statement from the documentation
Existing code that uses directiveResolvers could consider migrating to direct usage of mapSchema
Because they have different behavior when it comes to order of execution, I don't see how they are interchangeable.
Can someone explain if there is a way to guarantee that the Schema Directives execute in the order they are used in the schema for a particular field?
Please see this github issue for in depth discussion.
The new API doesn't work the same way as directiveResolvers or schemaDirectives. A schemaTransform is applied to the entire schema before the next one contrary to the other two in which the all the transforms are applied to a particular field before visiting the next field node. There are two approaches to this in my opinion:
Create a new #pipeline directive which takes a list of names of other directives and then applies them in the order like directiveResolvers.
I took a bit different route where I created a new function attachSchemaTransforms just like attachDirectiveResolvers which visits each node and applies all the directives in order.
export function attachSchemaTransforms(
schema: GraphQLSchema,
schemaTransforms: Record<string, FieldDirectiveConfig>, // a custom config object which contains the transform and the directive name
): GraphQLSchema {
if (typeof schemaTransforms !== 'object') {
throw new Error(`Expected schemaTransforms to be of type object, got ${typeof schemaTransforms}`);
}
if (Array.isArray(schemaTransforms)) {
throw new Error('Expected schemaTransforms to be of type object, got Array');
}
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: oldFieldConfig => {
const fieldConfig = { ...oldFieldConfig };
const directives = getDirectives(schema, fieldConfig);
Object.keys(directives).forEach(directiveName => {
const config = schemaTransforms[directiveName];
if (config) {
const { apply, name } = config;
const directives = getDirectives(schema, fieldConfig);
if (directives[name]) {
const directiveArgs: unknown = directives[name]
apply(fieldConfig, directiveArgs);
return fieldConfig;
}
}
});
return fieldConfig;
},
});
}

check if d3.select or d3.selectAll

I have a method on a reusable chart that can be passed a selection and return a value if it is passed a d3.select('#id') selection or an array of values if it is passed a d3.selectAll('.class') selection. I'm currently interrogating the passed argument with context._groups[0] instanceof NodeList, but it feels a little fragile using an undocumented property, as that may change in future versions. Is there a more built in way of determining if a selection comes from select or selectAll?
selection.size() will not help here, as it only tells us the result of the selection, not how it was called.
EDIT:
Here's the context of the use. I'm using Mike Bostock's reusable chart pattern and this instance includes a method for getting/setting a label for a donut.
To me, this API usage follows the principle of least astonishment, as it's how I would expect the result to be returned.
var donut = APP.rotatingDonut();
// set label for one element
d3.select('#donut1.donut')
.call(donut.label, 'Donut 1')
d3.select('#donut2.donut')
.call(donut.label, 'Donut 2')
// set label for multiple elements
d3.selectAll('.donut.group-1')
.call(donut.label, 'Group 1 Donuts')
// get label for one donut
var donutOneLabel = d3.select('#donut1').call(donut.label)
// donutOnelabel === 'Donut 1'
// get label for multiple donuts
var donutLables = d3.selectAll('.donut').call(donut.label)
// donutLabels === ['Donut 1', 'Donut 2', 'Group 1 Donuts', 'Group 1 Donuts']
and the internal method definition:
App.rotatingDonut = function() {
var label = d3.local();
function donut() {}
donut.label = function(context, value) {
var returnArray;
var isList = context._groups[0] instanceof NodeList;
if (typeof value === 'undefined' ) {
// getter
returnArray = context.nodes()
.map(function (node) {return label.get(node);});
return isList ? returnArray : returnArray[0];
}
// settter
context.each(function() {label.set(this, value);});
// allows method chaining
return donut;
};
return donut
}
Well, sometimes a question here at S.O. simply doesn't have an answer (it has happened before).
That seems to be the case of this question of yours: "Is there a more built in way of determining if a selection comes from select or selectAll?". Probably no.
To prove that, let's see the source code for d3.select and d3.selectAll (important: those are not selection.select and selection.selectAll, which are very different from each other).
First, d3.select:
export default function(selector) {
return typeof selector === "string"
? new Selection([[document.querySelector(selector)]], [document.documentElement])
: new Selection([[selector]], root);
}
Now, d3.selectAll:
export default function(selector) {
return typeof selector === "string"
? new Selection([document.querySelectorAll(selector)], [document.documentElement])
: new Selection([selector == null ? [] : selector], root);
}
As you can see, we have only two differences here:
d3.selectAll accepts null. That will not help you.
d3.selectAll uses querySelectorAll, while d3.select uses querySelector.
That second difference is the only one that suits you, as you know by now, since querySelectorAll:
Returns a list of the elements within the document (using depth-first pre-order traversal of the document's nodes) that match the specified group of selectors. The object returned is a NodeList. (emphasis mine)
And querySelector only...:
Returns the first Element within the document that matches the specified selector, or group of selectors.
Therefore, the undocumented (and hacky, since you are using _groups, which is not a good idea) selection._groups[0] instanceof NodeList you are using right now seems to be the only way to tell a selection created by d3.select from a selection created by d3.selectAll.

Microsoft Bot: Show options from database instead of enums

In the example bot implementations from Microsoft, they use enums to define options for dialog, as shown in the example below:
public enum LengthOptions { SixInch, FootLong };
public enum BreadOptions { NineGrainWheat, NineGrainHoneyOat, Italian, ItalianHerbsAndCheese, Flatbread };
Can we use a normal list to fetch the values from the database and display it as options?
Thanks
You can't do this out of the box, but you could subclass FormBuilderBase<T>, overriding various methods to build the Form using whatever datasource you prefer.
Edit:
You can find the base class and implementation of FormBuilder here: https://github.com/Microsoft/BotBuilder/blob/master/CSharp/Library/FormFlow/FormBuilder.cs
Basically, there are a mess of virtual methods that you can override to customize how you want to form to behave, but the main one is Build. In the default implementation, it iterates though the enums to create a list of Field, which are basically each step in you form. Instead of that, you can iterate through whatever data you have pulled from your database and create a new Field for each item. It may look something like this:
public override IForm<T> Build(Assembly resourceAssembly = null, string resourceName = null)
{
var list = GetListOfItemsFromDatabase();
foreach (var item in _list)
{
// FieldFromItem is an IField and will also need to be created
Field(new FieldFormItem<T>(item));
}
Confirm(new PromptAttribute(_form.Configuration.Template(TemplateUsage.Confirmation)));
}
return base.Build(resourceAssembly, resourceName);
}
I know its late but found myself struggling with the same and found that below would be the right solution for this.In your FormFlow class just add the Terms and Descriptions manually.From your example if we are talking about length options then change the type of LengthOptions to string add following code when you build the form.
return new FormBuilder<SandwichForm>()
.Field(new FieldReflector<SandwichForm>(nameof(LengthOptions))
.SetDefine(async (state, field) =>
{
// Call database and get options and iterate over the options
field
.AddDescription("SixInch","Six Inch")
.AddTerms("SixInch", "Six Inch")
.AddDescription("FootLong ","Foot Long")
.AddTerms("FootLong ", "Foot Long")
return true;
}))
.OnCompletion(completionDelegate)
.Build();

possible to print created filter for rethinkdb for debugging purposes?

Using node I'm trying to programmatically creating a filter which fails. Is there a way to check the output of a filter as it is passed to RethinkDB? That way I can try to debug the filter.
Background
I'm trying to write a helper that, given nested object notation a.b matches against a given value.
E.g.: I'm trying to create the following filter:
entry("a")("b").match(val)
Using the helper:
function iterateProp(entry, path, val) {
if (path.length) {
var propKey = path.shift();
return iterateProp(entry(propKey), path, val);
}
return entry(val);
}
Called as such:
Model.filter(function(entry) {
var modelKey = "a.b"; //illustrational
var val ="someValToMatch"; //illustrational
var filter = iterateProp(entry, modelKey.split("."), val);
console.log("filter", filter); //HOW TO DISPLAY FILTER AS CONSTRUCTED BY RETHINKDB DRIVER AND SEND TO RETHINKDB?
return filter;
})
This should just work in my book, but it doesn't. So how to display the constructed filter so I can do some debugging?
You can add a toString() at the end of the query (replacing run()) to see what the driver will send to the server.
console.log(r.table("test").filter(...).toString())

Resources