I am using jscodeshift to transform function calls:
foo() --> foo({uid: ... label: ...})
const newArgObj = j.objectExpression([
j.property(
'init',
j.identifier('uid'),
j.literal(getUID()),
),
j.property(
'init',
j.identifier('label'),
j.literal('bar'),
)
]);
node.arguments = [newArgObj];
...
return callExpressions.toSource({quote: 'single'});
The problem is objectExpression is always pretty-printed:
foo({
uid: 'LBL_btBeZETZ',
label: 'bar'
})
How to prevent that and get something like:
foo({uid: 'LBL_btBeZETZ', label: 'bar'})
ok, it is impossible, take a look at recast's printer.js source:
case "ObjectExpression":
case "ObjectPattern":
case "ObjectTypeAnnotation":
...
var len = 0;
fields.forEach(function(field) {
len += n[field].length;
});
var oneLine = (isTypeAnnotation && len === 1) || len === 0;
var parts = [oneLine ? "{" : "{\n"];
...
if (!oneLine) {
lines = lines.indent(options.tabWidth);
}
...
parts.push(oneLine ? "}" : "\n}");
WORKAROUND (at least in my simple case) - you can use raw string:
const rawCode = `{uid: '${getUID()}', label: 'bar' }`;
node.arguments = [rawCode];
or
node.arguments = [j.jsxText(rawCode)];
Both will give you:
foo({uid: 'LBL_btBeZETZ', label: "bar" })
Related
I am trying to overwrite Cypress commands such as click, type and should to include some waiting time before they are executed. My motivation for this is that I want to highlight the areas the test interacts with in the produced video, so in click I would like to say for example: "Display circle where the click will happen, wait 500ms, click, wait 250ms, remove circle".
The wait-part of this of this is what causes me trouble.
Google suggests I do something like this:
Cypress.Commands.overwrite('click', function (originalFN) {
const originalParams = [...arguments].slice(1);
cy.wait(500).then(() => originalFN.apply(originalFN, originalParams));
});
And I think this works for normal clicks(), but it causes the type command to fail entirely saying this: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.
It seems type() internally calls click in a way that prevents me from using wait() inside click.
Is there any way around this?
I've found a solution in the code of a library to slow down cypress, the key is to overwrite the internal runCommand cypress function. This allows me to do what I want on click and type. Should is still an open question, but not as important. Code below is my function to patch cypress, which I call right before my tests.
export function patchCypressForVideoRecording(cy: any, Cypress: any, speedFactor = 1) {
const colorClick = 'rgba(255, 50, 50, 0.8)';
const colorType = 'rgba(50, 255, 50, 0.8)';
const colorShould = 'rgba(50, 50, 255, 0.8)';
const waitTime = 600;
const highlightArea = (rect: any, clr: string, scale: boolean) => {
const x = Math.round(rect.x);
const y = Math.round(rect.y);
const w = Math.round(rect.width);
const h = Math.round(rect.height);
// cy.window() breaks in commands like click due to promise-inside promises stuff
// this window reference is just there and allows to run synchronous side-effects js without cypress noticing it
const hackWindow = (cy as any).state('window');
hackWindow.eval(`
const time = ${waitTime / speedFactor};
const x = ${x};
const y = ${y};
const highlightElement = document.createElement('div');
highlightElement.style.backgroundColor = '${clr}';
highlightElement.style.position = 'fixed';
highlightElement.style.zIndex = '999';
highlightElement.style['pointer-events'] = 'none';
document.body.appendChild(highlightElement);
const scaleElement = (p) => {
if (${scale}) {
const psq = p;
const scale = (0.1 + ((psq < 0.5 ? (1 - psq) : psq)));
const w = scale * ${w};
const h = scale * ${h};
const wLoss = ${w} - w;
const hLoss = ${h} - h;
const x = ${x} + wLoss / 2;
const y = ${y} + hLoss / 2;
return {x, y, w, h};
} else {
const w = ${w};
const h = ${h};
const x = ${x};
const y = ${y};
return {x, y, w, h};
}
};
const newSize = scaleElement(0);
highlightElement.style.top = newSize.y + 'px';
highlightElement.style.left = newSize.x + 'px';
highlightElement.style.width = newSize.w + "px";
highlightElement.style.height = newSize.h + "px";
const tickSize = 30;
let op = 1;
let prog = 0;
const fadeIv = setInterval(() => {
prog += tickSize;
const p = Math.min(1, prog / time);
let op = 1-(p*0.5);
highlightElement.style.opacity = op + '';
const newSize = scaleElement(p);
highlightElement.style.top = newSize.y + 'px';
highlightElement.style.left = newSize.x + 'px';
highlightElement.style.width = newSize.w + "px";
highlightElement.style.height = newSize.h + "px";
}, tickSize);
setTimeout(() => {
clearInterval(fadeIv);
document.body.removeChild(highlightElement);
}, time);
`);
};
const highlightInteractedElements = (firstParam: any, clr: string, scale: boolean) => {
if (firstParam != null && firstParam.length != null && firstParam.length > 0 && typeof firstParam !== 'string') {
for (let i = 0; i < firstParam.length; i++) {
const elem = firstParam[i];
if (elem != null && 'getBoundingClientRect' in elem && typeof elem['getBoundingClientRect'] === 'function') {
highlightArea(elem.getBoundingClientRect(), clr, scale);
}
}
}
};
// To figure out the element that is clicked/typed in need to wait until
// the selector right before click/type has a subject element
const waitAndDisplay = (x: any, clr: string) => {
if (x.state === 'passed') {
highlightInteractedElements(x.attributes.subject, clr, true);
} else {
if (x.attributes.prev.state === 'queued') {
setTimeout(() => {
waitAndDisplay(x, clr);
}, 15);
} else {
highlightInteractedElements(x.attributes.prev.attributes.subject, clr, true);
}
}
};
const cqueue = (cy as any).queue;
const rc = cqueue.runCommand.bind(cqueue);
cqueue.runCommand = (cmd: any) => {
let delay = 50;
if (cmd.attributes.name === 'click') {
waitAndDisplay(cmd, colorClick);
delay = waitTime / 2;
}
if (cmd.attributes.name === 'type') {
waitAndDisplay(cmd, colorType);
delay = waitTime;
}
return Cypress.Promise.delay(delay / speedFactor)
.then(() => rc(cmd))
.then(() => Cypress.Promise.delay(delay / speedFactor));
};
Cypress.Commands.overwrite('should', function (originalFN: any) {
const originalParams = [...arguments].slice(1);
highlightInteractedElements(originalParams[0], colorShould, false);
return originalFN.apply(originalFN, originalParams);
});
}
I'm trying to filter a collection by a multireference field before the function does its job.
I used this wix example but I don't want it to filter the whole collection https://www.wix.com/corvid/example/filter-with-multiple-options
I'm new at this and probably doing it wrong this is what i managed to figure out
import wixData from 'wix-data';
const collectionName = 'Blog/Posts'
//const collectionName = wixData.query('Blog/Posts').contains("categories", ["O -Fitness"]);
const fieldToFilterByInCollection = 'hashtags';
$w.onReady(function () {
setRepeatedItemsInRepeater()
loadDataToRepeater()
$w('#tags').onChange((event) => {
const selectedTags = $w('#tags').value
loadDataToRepeater(selectedTags)
})
});
function loadDataToRepeater(selectedCategories = []) {
let dataQuery = wixData.query(collectionName)//.contains("categories", ["O -Fitness"]);
if (selectedCategories.length > 0) {
dataQuery = dataQuery.hasAll(fieldToFilterByInCollection, selectedCategories)
}
dataQuery
.find()
.then(results => {
const itemsReadyForRepeater = results.items
$w('#Stories').data = itemsReadyForRepeater;
const isRepeaterEmpty = itemsReadyForRepeater.length === 0
if (isRepeaterEmpty) {
$w('#noResultsFound').show()
} else {
$w('#noResultsFound').hide()
}
})
}
function setRepeatedItemsInRepeater() {
$w('#Stories').onItemReady(($item, itemData) => {
$item('#image').src = itemData.coverImage;
$item('#title').text = itemData.title;
if ($item("#title").text.length > 40){
$item("#title").text =$item("#title").text.slice(0, 40) + '...' ;}
$item('#excerpt').text = itemData.excerpt;
if ($item('#excerpt').text.length > 100){
$item('#excerpt').text =$item('#excerpt').text.slice(0, 100) + '...' ;}
})
}
its this commented bit I'm trying to add
const collectionName = wixData.query('Blog/Posts').contains("categories", ["O -Fitness"]);
Thanks in advance
You used 'hasAll' to filter multi-refernce field. 'hasSome' working on multi-refernce but 'hasAll' isnt working on this field type.
you can use:
selectedCategories.map(category => {
dataQuery = dataQuery.hasSome(fieldToFilterByInCollection, category)
})
which is the same as hasAll - hasSome(x) & hasSome(Y) = hasAll(x,y) - but beacuse 'hasSome' is working on multirefernce it will work:)
I want to tell GraphQL that my 'serialNumber' field is a String with a length of between 1 and 20 characters. I know to use the ! in the schema to make a field Required, but can I tell GraphQL that there is also a maximum field length?
If I pass a null value in for a required String field, GraphQL will not accept it. I want it to behave the same way when it is passed a string that is longer than the maximum allowed length.
I have looked at doing this two ways: 1) Add a 'maxlength' attribute to the field definition in the schema.graphql file. or 2) Create a new type and assign it a maximum length.
I can't find any information on how to do it. Is is possible?
You can use directives:
directive #length(max: Int!) on FIELD_DEFINITION
input Payload {
name: String! #length(max: 50)
}
There is no place for this type of specification in graphql (types) definitions.
You can use directives but they are not supported (and not standarized) by all graphql servers - it's left to specific implementation details - check your server docs.
For single fields you can simply apply this logic inside mutation resolver - just throw an error if conditions (input string length between 1 and 20) not met.
This is possible to implement using GraphQL custom types.
You can define a type for each maximum length you want:
StringMaxLenTypes.js:
const logToConsoleToHelpMeUnderstand = false;
const { GraphQLScalarType, Kind } = require('graphql');
const parseStringMaxLenType = (value,maxLength) => {
logToConsoleToHelpMeUnderstand && console.log('Checking variable passed to a query is a string and max ' + maxLength + 'chars',value)
if (typeof value === 'string') {
if (value.length <= maxLength) {
return value;
} else {
throw new Error('parseCi' + maxLength + 'Type: String must not be more that ' + maxLength + ' characters. It is ' + value.length + ' characters');
}
} else {
throw new Error('parseCi' + maxLength + 'Type: value must be of type String. It is of type \'' + typeof value + '\'');
}
}
const parseStringMaxLen20Type = value => { return parseStringMaxLenType(value, 20) };
const parseStringMaxLen25Type = value => { return parseStringMaxLenType(value, 25) };
const parseStringMaxLen50Type = value => { return parseStringMaxLentype(value, 50) };
const parseStringMaxLen255Type = value => { return parseStringMaxLentype(value, 255) };
const parseStringMaxLen500Type = value => { return parseStringMaxLentype(value, 500) };
const serializeStringMaxLenType = (value, maxLength) => {
/** the serialize methods are called when data is going to be sent to the client. This can return anything
* of any type, as it will end up as JSON, but we check what is coming back from the database is a string
* and not too long.
*/
logToConsoleToHelpMeUnderstand && console.log(`Checking value '${value}' is at most ${maxLength} chars before serialising for output from graphQL to a client (the OCCP gateway is the client)`);
if (typeof value === 'string') {
if (value.length <= maxLength) {
return value;
} else {
throw new Error('serializeCi' + maxLength + 'Type: String must not be more that ' + maxLength + ' characters. It is ' + value.length + ' characters');
}
} else {
throw new Error('serializeCi' + maxLength + 'Type: value must be of type String. It is of type \'' + typeof value + '\'');
}
};
const serializeStringMaxLen20Type = (value) => { return serializeStringMaxLenType(value,20) };
const serializeStringMaxLen25Type = (value) => { return serializeStringMaxLenType(value,25) };
const serializeStringMaxLen50Type = (value) => { return serializeStringMaxLenType(value,50) };
const serializeStringMaxLen255Type = (value) => { return serializeStringMaxLenType(value,255) };
const serializeStringMaxLen500Type = (value) => { return serializeStringMaxLenType(value,500) };
const parseLiteralStringMaxLenType = (ast, maxLength) => {
logToConsoleToHelpMeUnderstand && console.log('checking Abstract Syntax Tree value (these come from parameters in GraphQL queries) is not more than ' + maxLength + ' chars long',ast);
// For input payload i.e. for mutation. ast stands for abstract syntax tree, which is the type of
if (ast.kind === Kind.STRING) {
// Note the parseStringMaxLenType function throws errors, or returns a valid value, so there is no need to throw errors here.
return parseStringMaxLenType(ast.value, maxLength)
} else {
throw new Error()
}
};
const parseLiteralStringMaxLen20Type = (ast) => { return parseLiteralStringMaxLenType(ast, 20) }
const parseLiteralStringMaxLen25Type = (ast) => { return parseLiteralStringMaxLenType(ast, 25) }
const parseLiteralStringMaxLen50Type = (ast) => { return parseLiteralStringMaxLenType(ast, 50) }
const parseLiteralStringMaxLen255Type = (ast) => { return parseLiteralStringMaxLenType(ast, 255) }
const parseLiteralStringMaxLen500Type = (ast) => { return parseLiteralStringMaxLenType(ast, 500) }
const StringMaxLen20Type = new GraphQLScalarType({
name: 'StringMaxLen20Type',
description: 'String up to 20 Chars',
serialize: serializeStringMaxLen20Type,
parseValue: parseStringMaxLen20Type,
parseLiteral: parseLiteralStringMaxLen20Type,
});
const StringMaxLen25Type = new GraphQLScalarType({
name: 'StringMaxLen25Type',
description: 'String up to 25 Chars',
serialize: serializeStringMaxLen25Type,
parseValue: parseStringMaxLen25Type,
parseLiteral: parseLiteralStringMaxLen25Type,
});
const StringMaxLen50Type = new GraphQLScalarType({
name: 'StringMaxLen50Type',
description: 'String up to 50 Chars',
serialize: serializeStringMaxLen50Type,
parseValue: parseStringMaxLen50Type,
parseLiteral: parseLiteralStringMaxLen50Type,
});
const StringMaxLen255Type = new GraphQLScalarType({
name: 'StringMaxLen255Type',
description: 'String up to 255 Chars',
serialize: serializeStringMaxLen255Type,
parseValue: parseStringMaxLen255Type,
parseLiteral: parseLiteralStringMaxLen255Type,
});
const StringMaxLen500Type = new GraphQLScalarType({
name: 'StringMaxLen500Type',
description: 'String up to 500 Chars',
serialize: serializeStringMaxLen500Type,
parseValue: parseStringMaxLen500Type,
parseLiteral: parseLiteralStringMaxLen500Type,
});
module.exports = {StringMaxLen20Type, StringMaxLen25Type, StringMaxLen50Type, StringMaxLen255Type, StringMaxLen500Type };
Then you need to define each of these types in your grapql.shema
schema.graphql:
scalar StringMaxLen20Type
scalar StringMaxLen25Type
scalar StringMaxLen50Type
scalar StringMaxLen255Type
scalar StringMaxLen500Type
type YourOtherTypes {
id: ID!
canUseAboveTypesLikeThis: StringMaxLen50Type
}
And import them into your resolvers.js, and define them in the resolver object passed to your graphQL server :
resolvers.js:
...
...
const {StringMaxLen20Type, StringMaxLen25Type, StringMaxLen50Type, StringMaxLen255Type, StringMaxLen500Type } = require('StringMaxLenTypes.js');```
...
module.exports = {
Query: {...},
Mutation: {...},
StringMaxLen20Type,
StringMaxLen25Type,
StringMaxLen50Type,
StringMaxLen255Type,
StringMaxLen500Type
This will then apply the same strict checking that GraphQL does all its data types to the maximum lengths.
I wrote JQuery to grab columns' text in the ColumnChooser pop-up dialog, in order to get colModel's Name (or Index) then I learned it doesn't work that way and I have to somehow use colName against colModel instead.
Problem..
colNames: [ 'Id', 'Stock Number', 'VIN', 'Year' ],
colModel: [
{ name: 'Id', index: 'Id' },
{ name: 'StockNumber', index: 'StockNumber' },
{ name: 'VIN', index: 'VIN' },
{ name: 'Year', index: 'Year' }
As you can see my problem is "Stock Number" is not the same as "StockNumber" when using $ColumnChooserSelectedList against the $jqgridColumnModelSetting. Also, I cannot tell if columns are in proper order (between colName & colModel) as I don't know how it works behind the scene.
var $ColumnChooserSelectedList = $("#colchooser_test ul.selected li");
var $ColumnModelSetting = $("#test").jqGrid('getGridParam', 'colModel');
var returnValue = "";
$.each($ColumnChooserSelectedList, function (i,o) {
if (o.title.length > 0) {
if (returnValue.length > 0) { returnValue += "|"; }
returnValue += o.title; //This o.title need to be changed to match colModel's Name (or Index)...
}
});
Thanks...
Updated - Solution found
Came up with this nice solution but I cannot be sure if it works 100% of the time.
var $ColumnChooserSelectedList = $("#colchooser_test ul.selected li");
var $ColumnModelSetting = $("#test").jqGrid('getGridParam', 'colModel');
var $ColumnNameSetting = $("#test").jqGrid('getGridParam', 'colNames');
var returnValue = "";
$.each($ColumnChooserSelectedList, function (i1,o1) {
if (o1.title.length > 0) {
$.each($ColumnNameSetting, function (i2, o2) {
if ($ColumnNameSetting[i2] == o1.title) {
if (returnValue.length > 0) { returnValue += "|"; }
returnValue += $ColumnModelSetting[i2].name;
return false; //This break the foreach loop...
}
});
}
});
Updated - Solution found
Came up with this nice solution but I cannot be sure if it works 100% of the time.
var $ColumnChooserSelectedList = $("#colchooser_test ul.selected li");
var $ColumnModelSetting = $("#test").jqGrid('getGridParam', 'colModel');
var $ColumnNameSetting = $("#test").jqGrid('getGridParam', 'colNames');
var returnValue = "";
$.each($ColumnChooserSelectedList, function (i1,o1) {
if (o1.title.length > 0) {
$.each($ColumnNameSetting, function (i2, o2) {
if ($ColumnNameSetting[i2] == o1.title) {
if (returnValue.length > 0) { returnValue += "|"; }
returnValue += $ColumnModelSetting[i2].name;
return false; //This break the foreach loop...
}
});
}
});
I have a grid in ext with some custom columns, and I want to be able to sort this column - I want to sort it by what is displayed inside of it, but really I just cannot figure out how to define a sorter for a column that will not be based on the dataIndex - I tried using a custom model, but I could not get that to work.
{
text: 'Parent',
dataIndex: 'Parent',
renderer: function(value, meta, record) {
var ret = record.raw.Parent;
if (ret) {
return ret.Name;
} else {
meta.tdCls = 'invisible';
return record.data.Name;
}
},
sortable: true
},
You should be able to override the doSort method of the column. Here's the gist of it. I also created a working fiddle (http://jsfiddle.net/cfarmerga/LG5uA/). The fiddle uses the string length of a field as the property to sort on, but of course you could apply your own custom sort logic.
var grid = Ext.create('Ext.grid.Panel',{
//...
columns: [
{ text: 'name', dataIndex: 'name', sortable: true },
{
text: 'Custom',
sortable : true,
dataIndex: 'customsort',
doSort: function(state) {
var ds = this.up('grid').getStore();
var field = this.getSortParam();
ds.sort({
property: field,
direction: state,
sorterFn: function(v1, v2){
v1 = v1.get(field);
v2 = v2.get(field);
return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
}
});
}
}
]
//....
});
For Ext JS version 5, it looks like doSort was taken out, so I couldn't override that. Instead, I went the route of listening to the sortchange event, and from there, I used the Ext.data.Store.setSorters method. The code is a bit custom, and overly complex because of the data that I'm using, so keep that in mind (Fiddle here):
// grid class
initComponent: function() {
...
this.on('sortchange', this.onSortChange, this);
},
onSortChange: function(container, column, direction, eOpts) {
// check for dayColumnIndex
if (column && column.dayColumnIndex !== undefined) {
this.sortColumnByIndex(column.dayColumnIndex, direction);
}
},
sortColumnByIndex: function(columnIndex, direction) {
var store = this.getStore();
if (store) {
var sorterFn = function(rec1, rec2) {
var sortValue = false;
if (rec1 && rec2) {
var day1;
var daysStore1 = rec1.getDaysStore();
if (daysStore1) {
day1 = daysStore1.getAt(columnIndex);
}
var day2;
var daysStore2 = rec2.getDaysStore();
if (daysStore2) {
day2 = daysStore2.getAt(columnIndex);
}
if (day1 && day2) {
var val1 = day1.get('value');
var val2 = day2.get('value');
sortValue = val1 > val2 ? 1 : val1 === val2 ? 0 : -1;
}
}
return sortValue;
};
if (direction !== 'ASC') {
sorterFn = function(rec1, rec2) {
var sortValue = false;
if (rec1 && rec2) {
var day1;
var daysStore1 = rec1.getDaysStore();
if (daysStore1) {
day1 = daysStore1.getAt(columnIndex);
}
var day2;
var daysStore2 = rec2.getDaysStore();
if (daysStore2) {
day2 = daysStore2.getAt(columnIndex);
}
if (day1 && day2) {
var val1 = day1.get('value');
var val2 = day2.get('value');
sortValue = val1 < val2 ? 1 : val1 === val2 ? 0 : -1;
}
}
return sortValue;
};
}
store.setSorters([{
sorterFn: sorterFn
}]);
}
}
There is a convert method on the Ext.data.Model class that allows you to convert the data before it's being used. Then you can just specify this 'dataIndex' in your column and do a normal sort. The column will be sorted by that converted value. Here is the a sample model with just one field (Parent) and with it's corresponding conversion:
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'Parent', type: 'string', convert: sortParent},
// other fields...
],
sortParent: function(value, record) {
var ret = record.raw.Parent;
if (ret) {
return ret.Name;
} else {
meta.tdCls = 'invisible';
return record.data.Name;
}
}
});