Cypress cucumber test cannot see custom Chai.Assertion and causes build error - chai

I've a NodeJS React project with tests written using Cypress and the "cypress-cucumber-preprocessor".
I added a custom Chai Assertion in /tests/cypress/support/index.js:
//ref: https://stackoverflow.com/questions/55842707/how-can-i-define-a-custom-assertion-operator-in-cypress
chai.Assertion.addMethod("beToday", function () {
const dateValue = this._obj
//new chai.Assertion(dateValue).to.be.exist
const date = dateValue instanceof Date ? dateValue : new Date(dateValue)
const today = new Date()
const expected = today.toISOString().substring(0, 10)
const isOk = date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate()
this.assert(
isOk
, "expected #{act} to be today"
, expected
, dateValue
);
})
The assertion is used in /tests/cypress/integration/Components/MyComponent/MyComponent.ts):
It seems not to "see" the custom assertion but when I run the Cypress tests it is working fine, passing the test when the value is correct and failing with the right error otherwise:
My problem is the intellisense in VS Code that does not recognize the custom assertion and also the yarn build command fails.

Related

karate.match is throwing a TypeError when validating against a string

I am doing some UI automation using Karate. In this particular scenario, the goal is to navigate to a page that has a table with pagination. If there is a "next page" button, to click it, and to verify the pagination changes as expected (pagination will always return either an 8 or a 9).
I have the following code:
* def getPagination =
"""
function(){
var nameVal = []
var nextPageBtn = false
do {
nextPageBtn = driver.exists('skipToNext a');
if (nextPageBtn {
driver.click('skipToNext a');
var getPagination = (driver.text(li[class='active'] a)).trim()
karate.log("The current page value is:", getPagination)
karate.match (getPagination == '8' || '9')
var getNameVal = driver.scriptAll('#names', '_.textContent')
nameVal.push(getNameVal)
}
} while (nextPageBtn)
return(nameVal);
}
I am getting the following error on the match:
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (match) on com.intuit.karate.core.ScenarioBridge#5d32d8f failed due to: no applicable overload found (overloads: [Method[public java.lang.Object com.intuit.karate.core.ScenarioBridge.match(java.lang.String)], Method[public java.lang.Object com.intuit.karate.core.ScenarioBridge.match(java.lang.Object,java.lang.Object)]], arguments: [true (Boolean)])
- <js>.:anonymous(Unnamed:17)
Any ideas?

How to pass an array as a parameter to statementExecPromisified(statement,[]) function of sap-hdbext-promisfied in nodejs?

I have an array of users as below
let usersarr = ["'SAC_XSA_HDB_USER_ABC','SAC_XSA_HDB_USER_DEF'"]
I want to fetch data about the above users(if exists) from Hana database. I am using sap-hdbext-promisfied library in node.js.
My database connection is working fine. So, I am trying to execute a select query as below
async function readUsers(xsaDbConn){
try{
let usersarr = ["'SAC_XSA_HDB_USER_ABC','SAC_XSA_HDB_USER_DEF'"]
const checkuserexiststatement = await xsaDbConn.preparePromisified("SELECT USER_NAME FROM USERS WHERE USER_NAME IN (?)")
let checkuserexistresult = await xsaDbConn.statementExecPromisified(checkuserexiststatement, [usersarr])
console.log(checkuserexistresult)
return checkuserexistresult
}catch(err){
console.log(err)
return;
}
}
Below is the output I get
PS C:\Users\Documents\XSA\SAC_POC\cap_njs> npm start
> cap_njs#1.0.0 start C:\Users\Documents\XSA\SAC_POC\cap_njs
> node server.js
myapp is using Node.js version: v12.18.3
myapp listening on port 3000
[]
I get an empty array object as output. This is not the expected output, instead it should provide details about the users as they exist in the database.
The above code works when I provide single user value instead of multiple users in an array as shown below
async function readUsers(xsaDbConn, tempxsahdbusers){
try{
let usersarr = 'SAC_XSA_HDB_USER_ABC'
const checkuserexiststatement = await xsaDbConn.preparePromisified("SELECT USER_NAME FROM USERS WHERE USER_NAME IN (?)")
let checkuserexistresult = await xsaDbConn.statementExecPromisified(checkuserexiststatement, [usersarr])
console.log(checkuserexistresult)
return checkuserexistresult
}catch(err){
console.log(err)
return;
}
}
Output Of Above Code -
PS C:\Users\Documents\XSA\SAC_POC\cap_njs> npm start
> cap_njs#1.0.0 start C:\Users\Documents\XSA\SAC_POC\cap_njs
> node server.js
myapp is using Node.js version: v12.18.3
myapp listening on port 3000
[ 'SAC_XSA_HDB_USER_ABC' ]
So, why is it giving an empty array object when I provide an array as a parameter instead of a variable? Is it possible to provide an array as a parameter to the function statementExecPromisified(statement, []) of sap-hdbext-promisfied library in node.js ?
Your
let usersarr = ["'SAC_XSA_HDB_USER_ABC','SAC_XSA_HDB_USER_DEF'"]
has exactly one value, the String:
"'SAC_XSA_HDB_USER_ABC','SAC_XSA_HDB_USER_DEF'"
When passing the userarr in the statementExecPromisified function as a parameter you are actually passing a nested array in an array. You could either try
xsaDbConn.statementExecPromisified(checkuserexiststatement, [usersarr[0]])
or separate the values in the userarr and add multiple ? in the prepared statement and reference each single value with userarr[x].

[Vue warn]: Invalid prop: custom validator check failed for prop "colspan" in bootstrapvue

I am using BootstrapVue in my Vuejs project, I face a weired issue "Invalid prop" with b-table-simple componentinb-thead table helper I bind colspan with array length which always gives a number, and it works fine but it generates console warning message:
[Vue warn]: Invalid prop: custom validator check failed for prop "colspan".
found in
---> <BTh>
<BTr>
<BThead>
<BTableSimple>
<NationalTrends> at resources/js/components/trends/NationalTrends.vue
<Trends> at resources/js/components/trends/Trends.vue
<Root>
When I put number (4 or any other number) it works fine without generating the warning in the console.
Below is my code:
<template>
<div>
<b-table-simple hover small caption-top responsive striped>
<caption>Commodity Trends</caption>
<b-thead head-variant="light">
<b-tr>
<b-th>Commodity</b-th>
<b-th>Current Month</b-th>
<b-th :colspan="selected_periods.length">Previous Period</b-th>
<b-th :colspan="selected_periods.length">% Change From the Previous Period</b-th>
<b-th :colspan="selected_periods.length" class="text-center">Direction of Change</b-th>
</b-tr>
</div>
</template>
Please help, I spent one hour trying to figure out whats the problem but no luck......
Try to use like this:
:colspan="selected_periods.length > 0 ? selected_periods.length : 1"
Please check selected_periods.length against that custom validator (see bootstrap-vue source)
const digitsRx = /^\d+$/
// Parse a rowspan or colspan into a digit (or null if < 1 or NaN)
const parseSpan = val => {
val = parseInt(val, 10)
return digitsRx.test(String(val)) && val > 0 ? val : null
}
/* istanbul ignore next */
const spanValidator = val => isUndefinedOrNull(val) || parseSpan(val) > 0
export const isUndefined = val => val === undefined
export const isNull = val => val === null
export const isUndefinedOrNull = val => isUndefined(val) || isNull(val)
I managed to remove the warning by adding a default value if null (which will not happen in my code) as below:
<b-th :colspan="selected_periods.length || 4">Previous Period</b-th>
<b-th :colspan="selected_periods.length || 4">% Change From the Previous Period</b-th>
<b-th :colspan="selected_periods.length || 4" class="text-center">Direction of Change</b-th>

Gulp: Error - "Task function must be specified"

When I try to run the gulp command:
gulp dist --Layout="vertical-dark-menu-template" --LayoutName="vertical-dark-menu-template" --ThemeName="vertical-dark-menu-template" --TextDirection="LTR"
I get an error, one of which is reoccurring.
I've already tried reinstalling gulp and npm.
Here is my gulpfile.js:
// Require the modules.
var gulp = require('gulp');
var gutil = require('gulp-util');
var gulpSequence = require('gulp-sequence');
var gulpRequireTasks = require('gulp-require-tasks');
var minimist = require('minimist');
var config = require('./config.json');
var options = minimist(process.argv.slice(2));
// Global Variables
global.myLayout = options.Layout;
global.myLayoutName = options.LayoutName;
global.myThemeName = options.ThemeName;
global.config = config;
global.dashboardRename = '';
global.rtl = '';
global.dist = "false";
if( options.dist !== undefined ){
dist = options.dist;
}
if (options.TextDirection !== undefined){
global.myTextDirection = options.TextDirection.toLowerCase();
if (myTextDirection == 'rtl')
rtl = '-rtl';
}
else{
global.myTextDirection = '';
}
gutil.log(gutil.colors.green('Starting Gulp!!'));
// Exclude template specific files
if (myLayout == 'vertical-modern-menu-template') {
dashboardRename = config.vertical_modern_menu_template.dashboardRename;
pugSrc = config.vertical_modern_menu_template.pugSrc;
} else if (myLayout == 'vertical-menu-nav-dark-template') {
dashboardRename = config.vertical_menu_nav_dark_template.dashboardRename;
pugSrc = config.vertical_menu_nav_dark_template.pugSrc;
} else if (myLayout == 'vertical-gradient-menu-template') {
dashboardRename = config.vertical_gradient_menu_template.dashboardRename;
pugSrc = config.vertical_gradient_menu_template.pugSrc;
} else if (myLayout == 'vertical-dark-menu-template') {
dashboardRename = config.vertical_dark_menu_template.dashboardRename;
pugSrc = config.vertical_dark_menu_template.pugSrc;
} else if (myLayout == 'horizontal-menu-template') {
dashboardRename = config.horizontal_menu_template.dashboardRename;
pugSrc = config.horizontal_menu_template.pugSrc;
}
// Invoke the module with options.
gulpRequireTasks({
// Specify path to your tasks directory.
path: process.cwd() + '/gulp-tasks' // This is default!
// Additionally pass any options to it from the table below.
// ...
// path - './gulp-tasks' Path to directory from which to load your tasks modules
// separator - : Task name separator, your tasks would be named, e.g. foo:bar:baz for ./tasks/foo/bar/baz.js
// arguments - [] Additional arguments to pass to your task function
// passGulp - true Whether to pass Gulp instance as a first argument to your task function
// passCallback - true Whether to pass task callback function as a last argument to your task function
// gulp - require('gulp') You could pass your existing Gulp instance if you have one, or it will be required automatically
});
// Clean Task.
gulp.task('dist-clean', ['clean:css', 'clean:js']);
// Monitor changes for both pug and sass files.
gulp.task('monitor', gulpSequence(['sass:watch']));
// JS Distribution Task.
gulp.task('dist-js', gulpSequence('clean:js', 'copy:js', 'uglify:min', 'notify:js'));
// SASS Compile Task.
gulp.task('sass-compile', function(callback) {
gulpSequence('file-write:sass-file', 'file-write:sass-page-variable-file', ['sass:main', 'sass:theme', 'sass:layouts', 'sass:pages', 'sass:custom'], 'autoprefixer:css', 'csscomb:css', 'cssmin:css')(callback)
});
// CSS Distribution Task.
gulp.task('dist-css', gulpSequence('sass-compile', 'notify:css'));
// Full Distribution Task.
gulp.task('dist', ['dist-css', 'dist-js']);
// Default Task.
gulp.task('default', ['dist']);
gulp.task('replacement', gulpSequence('replacement:css', 'replacement:js'));
I expect the command to run successfully to generate JS and CSS files but instead an error is thrown. These specific JS and CSS files that I am attempting to generate allow a template that I am using to function properly. The specific error message that I keep on receiving when I run the designated command is:
assert.js:339
throw err;
^
AssertionError [ERR_ASSERTION]: Task function must be specified
at Gulp.set [as _setTask] (C:\Users\chann\Documents\GitHub\integratedesports.com\materialize-admin-template\node_modules\undertaker\lib\set-task.js:10:3)
at Gulp.task (C:\Users\chann\Documents\GitHub\integratedesports.com\materialize-admin-template\node_modules\undertaker\lib\task.js:13:8)
at Object.<anonymous> (C:\Users\chann\Documents\GitHub\integratedesports.com\materialize-admin-template\gulpfile.js:71:6)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Module.require (internal/modules/cjs/loader.js:692:17)
at require (internal/modules/cjs/helpers.js:25:18)

react-native .toLocaleString() not working on android

Updated 2022: With hermes enabled you should be good now.
I'm using .toLocaleString() on react-native for my number output. All work on IOS but seems not working on Android. This is normal or? Do I need to use a function for the decimal?
rather than using a polyfill or an external dependency, change the JSC your android app builds with. For the newer versions of react-native add or override the following line in app/build.gradle
def jscFlavor = 'org.webkit:android-jsc-intl:+'
On newer versions of RN >0.62 you can change the JSC (JavaScriptCore) build variant to support/include ICU i18n library and necessary data allowing to use e.g. Date.toLocaleString and String.localeCompare
Replace this line in your android/app/build.gradle file
def jscFlavor = 'org.webkit:android-jsc:+'
with this line
def jscFlavor = 'org.webkit:android-jsc-intl:+'
Clean build and react-native run android
Note
This variant is about 6MiB larger per architecture than default.
So, expect your APK size to increase by about 4MB for each APK architecture build if using def enableSeparateBuildPerCPUArchitecture = true and a more bigger APK if separate build per architecture is disabled
You can use
number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
This is an issue with Javascript core used to run react native in Android and not with react native itself. To overcome this, you'll have to integrate latest javascript core into your android build or upgrade react native to 0.59.
The details are documented in JSC Android Buildscripts repo.
Now for people who would like to do the locale string formatting without needing to integrate the entire javascript core, Javascript has Internationalization API which lets you format numbers to language sensitive format. Documentation available at MDN
This API is not available in android and needs to be polyfilled using Intl
In your project root, install the Intl library
yarn add intl
And then in your project's index file (index.js) add the following code at the top of the file:
if(Platform.OS === 'android') { // only android needs polyfill
require('intl'); // import intl object
require('intl/locale-data/jsonp/en-IN'); // load the required locale details
}
After doing the above two steps, you can now get locale string anywhere in your project using
new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(10000000);
In case you need to format number for another locale code, all the locale code details are available under the intl/locale-data/jsonp/ directory. Simply require the ones you need in your index.js file.
The reason for this is very old version of JavaScriptCore used by react-native. iOS embeds own version which is why it is working fine there.
Issue still exists (some reading about where it's heading https://github.com/facebook/react-native/issues/19737)
And more info about this from Airbnb devs
https://medium.com/airbnb-engineering/react-native-at-airbnb-the-technology-dafd0b43838 (search for "JavaScriptCore inconsistencies")
(value) => {
if (typeof value === 'number') {
const [currency, cents] = (value / 100).toFixed(2).toString().split('.');
return `${currency.replace(/\B(?=(\d{3})+(?!\d))/g, '.')},${cents}`;
}
return '0,00';
}
it's more recent and lightweight, please check
First install:
yarn add #formatjs/intl-getcanonicallocales #formatjs/intl-locale #formatjs/intl-pluralrules #formatjs/intl-numberformat
Check if need polyfill
import {shouldPolyfill} from '#formatjs/intl-numberformat/should-polyfill'
if (shouldPolyfill()) {
require('#formatjs/intl-getcanonicallocales/polyfill');
require('#formatjs/intl-locale/polyfill');
require('#formatjs/intl-pluralrules/polyfill');
require('#formatjs/intl-numberformat/polyfill');
require('#formatjs/intl-numberformat/locale-data/en-US');
}
see source: https://formatjs.io/docs/polyfills/intl-numberformat/
A very easy and straight forward way is to use a polyfill:
First it needs to be installed:
npm i number-to-locale-string-polyfill
This has to be added in your code, best just outside the class/function where you want to use .toLocaleString().
require('number-to-locale-string-polyfill');
I solved this using a custom function
function numberToMoney(amount, simbol = '$', decimalCount = 2, decimal
= ".", thousands = ",") {
decimalCount = Math.abs(decimalCount)
decimalCount = isNaN(decimalCount) ? 2 : decimalCount
const negativeSign = amount < 0 ? "-" : ""
const i = parseInt(amount = Math.abs(Number(amount) ||
0).toFixed(decimalCount)).toString()
const j = (i.length > 3) ? i.length % 3 : 0
return simbol + negativeSign + (j ? i.substr(0, j) + thousands : '') +
i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ?
decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "")
};
No need to install extra packages
Displaying currency values in React Native A zero dependencies solution:
const parseCurr = (value) =>
Platform.OS === 'android'
? '$' + price.toFixed(2)
: price.toLocaleString('en-US', { style: 'currency', currency:'USD' });
parseCurr(25.75) // => $25.75
A real life example (money values are multiplied by 100 for better cents precision) and converting the value to Brazilian Reais (R$)
export const getBRPrice = (price: number) => {
const parsedPrice =
( price / 100 ).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
return Platform.OS === 'android'
? `R$${ ( price / 100 ).toFixed(2) }`
: parsedPrice;
};
// getBRPrice(450) => R$4,50
Solution: 1
Go to your android/app/build.gradle
Replace this line def jscFlavor = 'org.webkit:android-jsc:+'
with this
def jscFlavor = 'org.webkit:android-jsc-intl:+'
Stop the metro and rebuild your app.
Solution: 2
Otherwise, you can use this package https://www.npmjs.com/package/luxon
import import {DateTime} from 'luxon';
const date = DateTime.fromISO(new Date().toISOString());
const formatted = date.toLocaleString(DateTime.DATETIME_MED);
console.log(formatted);
Merging some responses from this thread, you can use this code where it is possible to customize the formatted response
const defaultOptions = {
significantDigits: 2,
thousandsSeparator: ',',
decimalSeparator: '.',
symbol: '$'
}
const currencyFormatter = (value, options) => {
if (typeof value !== 'number') value = 0.0
options = { ...defaultOptions, ...options }
value = value.toFixed(options.significantDigits)
const [currency, decimal] = value.split('.')
return `${options.symbol} ${currency.replace(
/\B(?=(\d{3})+(?!\d))/g,
options.thousandsSeparator
)}${options.decimalSeparator}${decimal}`
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}
This will remove commas after decimal point
If you need two digits after the decimal and always want to round down
you can use below code.
Math.floor(1233.31231231 * 100) / 100).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
To round differently check out this resource
If these solutions don't work for you... In my case, I was using React Native with the expo web simulator and wanted to format minutes with 2 characters ie. 00, 01, ... 10, 11, etc. My solution was to check if minutes contained one character, if so, prepend a "0".
... + (date.getMinutes().toString().length == 1 ? "0" : "") + date.getMinutes().toString()