Cypress data-testid not found - node.js

I am running a cypress test on a remote web app and the cy.get method fails to capture elements based on their data-testid .
This is a sample of the tests that I want to run :
describe('test: deny', () => {
it('I can deny', () => {
cy.visit('https://www.deepskydata.com/');
cy.wait(1000)
cy.get("[data-testid='uc-accept-all-button']").click();
});
});
Here the button with data-testid='uc-accept-all-button' is not found by cy.get.
Also cy.get('button') doesn't work despite that there are multiple button elements in the DOM.
Has anyone ever encountered a similar issue ?

The button you want to click is for privacy settings.
The problem is this section of the page isn't consistently displayed, once you click the "Accept All" button it may not display on the next run.
To avoid the problem, you should poll for the button inside the shadow root container element before attempting to click.
function clickPrivacySettingsButton(attempt = 0) {
// Poll for 4 seconds
if (attempt === 40) {
cy.log('No Accept All button to click')
return
}
cy.get('#usercentrics-root', {log:false}).then($privacySettings => {
const prvivacyShadow = $privacySettings.shadowRoot // get shadow root
const $acceptAllButton = Cypress.$(prvivacyShadow)
.find('[data-testid="uc-accept-all-button"]') // look for button
if ($acceptAllButton.length === 0) {
cy.wait(100, {log:false})
clickPrivacySettingsButton(++attempt)
} else {
cy.get('#usercentrics-root')
.shadow()
.find('[data-testid="uc-accept-all-button"]')
.click()
cy.log('Dismissed Accept All button')
return
}
})
}
cy.visit('https://www.deepskydata.com/')
clickPrivacySettingsButton()
Notes
You cannot just use Cypress includeShadowDom setting, because the shadow root may or may not exist.
You will have to apply cy.wait() in small increments to effectively poll for the Privacy Settings section.

I could see that there is a Shadow DOM in your app. TO make sure cypress traverses through the shadow DOM, in your cypress config file write:
includeShadowDom: true
Your cypress.config.js should look like this:
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
...
},
...
includeShadowDom: true
})
Then in your test, you can directly write:
describe('test: deny', () => {
it('I can deny', () => {
cy.visit('https://www.deepskydata.com/')
cy.get("[data-testid='uc-accept-all-button']").should('be.visible').click()
})
})
Avoid using cy.wait() as these can make the tests flaky. Instead use a visible assertion to first check that the element is visible and then click on it.

You may want to have a test to check the banner exists for a user meeting certain conditions.
Otherwise, you can bypass this modal, with correct permissions of course, by making a making a graphQL mutation saveConsents, so you will not need to making any checks at the UI level.
Note: you should choose your method of recursing and allow failOnStatusCode.
cy.request({
method: 'POST',
url: 'url-endpoint',
auth: 'any-authorization',
body: {
operationName: 'saveConsents',
query: 'graphQL mutation query'
}
variables: 'variables-if-needed',
failOnStatusCode: false, // to allow recurse
})

Related

How to write the jest test for onclick event im salesforce lwc

I have below code. How can i cover in the Jest salesforce?
testhandle(event) {
let testAbr = event.target.text;
if (testAbr) {
this.dispatchEvent(new CustomEvent('testpasss', { detail: { type: 'specilsnumber', input: testAbr } }));
}
}
this is coming on the click of user from UI. onclick={testhandle}
you can do a query of the markup element on which you have added the onclick handler method, and then dispatch a custom event on that element.
Example:
element.shadowRoot.querySelector('.hasOnclick').dispatchEvent(new CustomEvent('click'));
Make sure to add test assert in promise.resolve() to handle asynchronous DOM updates as below:
return Promise.resolve().then(() => {
expect(value).toBe(expected);
});

React Native: Reach-Navigation and Pouch-DB - db.put not done before "refresh" callback is run

Relative newbie; forgive me if my etiquette and form here aren't great. I'm open to feedback.
I have used create-react-native-app to create an application using PouchDB (which I believe ultimately uses AsyncStorage) to store a list of "items" (basically).
Within a TabNavigator (main app) I have a StackNavigator ("List screen") for the relevant portion of the app. It looks to the DB and queries for the items and then I .map() over each returned record to generate custom ListView-like components dynamically. If there are no records, it alternately displays a prompt telling the user so. In either case, there is an "Add Item" TouchableOpacity that takes them to a screen where they an add a new item (for which they are taken to an "Add" screen).
When navigating back from the "Add" screen I'm using a pattern discussed quite a bit here on SO in which I've passed a "refresh" function as a navigation param. Once the user uses a button on the "Add" screen to "save" the changes, it then does a db.post() and adds them item, runs the "refresh" function on the "List screen" and then navigates back like so:
<TouchableOpacity
style={styles.myButton}
onPress={() => {
if (this.state.itemBrand == '') {
Alert.alert(
'Missing Information',
'Please be sure to select a Brand',
[
{text: 'OK', onPress: () =>
console.log('OK pressed on AddItemScreen')},
],
{ cancelable: false }
)
} else {
this.createItem();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsScreen');
}
}
}
>
And all of this works fine. The "refresh" function (passed as onGoBack param) works fine... for this screen. The database is called with the query, the new entry is found and the components for the item renders up like a charm.
Each of the rendered ListItem-like components on the "List screen" contains a react-native-slideout with an "Edit" option. An onPress for these will send the user to an "Item Details" screen, and the selected item's _id from PouchDB is passed as a prop to the "Item Details" screen where loadItem() runs in componentDidMount and does a db.get(id) in the database module. Additional details are shown from a list of "events" property for that _id (which are objects, in an array) which render out into another bunch of ListItem-like components.
The problem arises when either choose to "Add" an event to the list for the item... or Delete it (using another function via [another] slideout for these items. There is a similar backward navigation, called in the same form as above after either of the two functions is called from the "Add Event" screen, this being the "Add" example:
async createEvent() {
var eventData = {
eventName: this.state.eventName.trim(),
eventSponsor: this.state.eventSponsor.trim(),
eventDate: this.state.eventDate,
eventJudge: this.state.eventJudge.trim(),
eventStandings: this.state.eventStandings.trim(),
eventPointsEarned: parseInt(this.state.eventPointsEarned.trim()),
};
var key = this.key;
var rev = this.rev;
await db.createEvent(key, rev, eventData);
}
which calls my "db_ops" module function:
exports.createEvent = function (id, rev, eventData) {
console.log('You called db.createEvent()');
db.get(id)
.then(function(doc) {
var arrWork = doc.events; //assign array of events to working variable
console.log('arrWork is first assigned: ' + arrWork);
arrWork.push(eventData);
console.log('then, arrWork was pushed and became: ' + arrWork);
var arrEvents = arrWork.sort((a,b)=>{
var dateA = new Date(a.eventDate), dateB = new Date(b.eventDate);
return b.eventDate - a.eventDate;
})
doc.events = arrEvents;
return db.put(doc);
})
.then((response) => {
console.log("db.createEvent() response was:\n" +
JSON.stringify(response));
})
.catch(function(err){
console.log("Error in db.createEvent():\n" + err);
});
}
After which the "Add Event" screen's button fires the above in similar sequence to the first, just before navigating back:
this.createEvent();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsDetails');
The "refresh" function looks like so (also called in componentDidMount):
loadItem() {
console.log('Someone called loadItem() with this.itemID of ' + this.itemID);
var id = this.itemID;
let totalWon = 0;
db.loadItem(id)
.then((item) => {
console.log('[LOAD ITEM] got back data of:\n' + JSON.stringify(item));
this.setState({objItem: item, events: item.events});
if (this.state.events.length != 0) { this.setState({itemLoaded: true});
this.state.events.map(function(event) {
totalWon += parseInt(event.eventPointsEarned);
console.log('totalWon is ' + totalWon + ' with ' +
event.eventPointsEarned + ' having been added.');
});
};
this.setState({totalWon: totalWon});
})
.catch((err) => {
console.log('db.loadItem() error: ' + err);
this.setState({itemLoaded: false});
});
}
I'm at a loss for why the List Screen refreshes when I add an item... but not when I'm doing other async db operations with PouchDB in what I think is similar fashion to modify the object containing the "event" information and then heading back to the Item Details screen.
Am I screwing up with Promise chain someplace? Neglecting behavior of the StackNavigator when navigating deeper?
The only other difference being that I'm manipulating the array in the db function in the non-working case, whereas the others I'm merely creating/posting or deleting/removing the record, etc. before going back to update state on the prior screen.
Edit to add, as per comments, going back to "List screen" and the opening "Item Details" does pull the database data and correctly shows that the update was made.
Further checking I've done also revealed that the console.log in createEvent() to print the response to the db call isn't logging until after some of the other dynamic rendering methods are getting called on the "Item Details" screen. So it seems as though the prior screen is doing the get() that loadItem() calls before the Promise chain in createEvent() is resolving. Whether the larger issue is due to state management is still unclear -- though it would make sense in some respects -- to me as this could be happening regardless of whether I've called my onGoBack() function.
Edit/bump: I’ve tried to put async/await to use in various places in both the db_ops module on the db.get() and the component-side loadItem() which calls it. There’s something in the timing of these that just doesn’t jive and I am just totally stuck here. Aside from trying out redux (which I think is overkill in this particular case), any ideas?
There is nothing to do with PDB or navigation, it's about how you manage outer changes in your depending (already mounted in Navigator since they are in history - it's important to understand - so componentDidMount isn't enough) components. If you don't use global state redux-alike management (as I do) the only way to let know depending component that it should update is passing corresponding props and checking if they were changed.
Like so:
//root.js
refreshEvents = ()=> { //pass it to DeleteView via screenProps
this.setState({time2refreshEvents: +new Date()}) //pass time2refreshEvents to EventList via screenProps
}
//DeleteView.js
//delete button...
onPress={db.deleteThing(thingID).then(()=> this.props.screenProps.refreshEvents())}
//EventList.js
...
constructor(props) {
super(props);
this.state = {
events: [],
noEvents: false,
ready: false,
time2refreshEvents: this.props.screenProps.time2refreshEvents,
}
}
static getDerivedStateFromProps(nextProps, currentState) {
if (nextProps.screenProps.time2refreshEvents !== currentState.time2refreshEvents ) {
return {time2refreshEvents : nextProps.screenProps.time2refreshEvents }
} else {
return null
}
}
componentDidMount() {
this._getEvents()
}
componentDidUpdate(prevProps, prevState) {
if (this.state.time2refreshEvents !== prevState.time2refreshEvents) {
this._getEvents()
}
}
_getEvents = ()=> {
//do stuff querying db and updating your list with actual data
}

Vuetify - how to make pagination?

I want to use pagination from Vuetify framework for VueJS.
Pagination component from Vuetify:
<v-pagination
v-model="pagination.page"
:length="pagination.total / 5"
:total-visible="pagination.visible"
></v-pagination>
I want to execute a function when the user clicks on a button. I want to get the page number and then execute the function with this page number in parameter.
Code from getItems from methods:
this.pagination.page = response.body.page
this.pagination.total = response.body.total
this.pagination.perPage = response.body.perPage
Data:
data () {
return {
items: [],
pagination: {
page: 1,
total: 0,
perPage: 0,
visible: 7
}
}
}
checkout the docs on the events section. I found the input event to handle new page.
<v-pagination
v-model="pagination.page"
:length="pagination.pages"
#input="next"
></v-pagination>
and my next method:
next (page) {
api.getBillList(page)
.then(response => {
this.bills = response.data.content
console.log(this.bills)
})
.catch(error => {
console.log(error)
})
}
COMMENT:
Before you implement pagination, try to see if you really need it in the first place, or you can use alternatives:
https://slack.engineering/evolving-api-pagination-at-slack-1c1f644f8e12
https://dzone.com/articles/why-most-programmers-get-pagination-wrong
http://allyouneedisbackend.com/blog/2017/09/24/the-sql-i-love-part-1-scanning-large-table/
https://www.xarg.org/2011/10/optimized-pagination-using-mysql/
https://www.eversql.com/faster-pagination-in-mysql-why-order-by-with-limit-and-offset-is-slow/
**ANSWER:**
You can react on pagination.page change with watcher since pagination.page changes on button click, and then execute your method.
watch: {
"pagination.page": (newPage) => {
this.onPageChange(newPage);
}
}
Or react on component's input event:
<v-pagination
#input="onPageChange"
></v-pagination>
I arrived here after searching for an error I received trying to implement this pagination in my VueJS project: [Vue warn]: Invalid prop: custom validator check failed for prop "length"..
My problem, and it looks like a problem you may have in your question's example code, was my calculation of length was arriving at a decimal answer. For example, if I had 23 records and a page size of 5, it would return 4.6, giving me the error above. I had to wrap my calculation in a Math.ceil() to arrive at the appropriate value for length.
Hope this helps someone :)
<v-pagination
v-model="currPage"
:length="Math.ceil(arr.length / pageSize)"
:total-visible="6"
></v-pagination>

Stacking of Context Menus in Electron

I am building an Electron based application that contains a grid containing unique rows. I would like a context-menu that is specific to each row. Here is an example:
Although this screen shot is cropped, you can see there are multiple rows and each row contains separate data. Since I'd like to right-click on a row and get a unique context menu, I have implemented electron-context-menu, which does work on the first right click, but then subsequent right-clicks causes a stacking effect of context menus.
Specifically, here is what happens:
I right click on Row-1 and the proper context menu shows up
I right click on Row-2 and a repeat of the context menu for Row-1 shows up then Row-2's context menu shows up. (Notice in the screen shot the context menu showing does not correspond to the row my mouse is over)
This repeats itself.
In React.JS, here is my listener, which collects the contextmenu object as needed by the electron-context-menu module:
handleContextMenu() {
this.props.contextMenu({
window: electron.remote.BrowserWindow.getFocusedWindow(),
prepend: (params, browserWindow) => [{
label: `Library Compare ${this.state.msn}`,
click: () => this.runLibCompare()
}],
append: (params, browserWindow) => [{
label: '---',
}]
})
};
Where this.props.contextMenu(...) perculates up the React.JS components to be fed into:
const contextMenu = eRequire('electron-context-menu');
I have done some massive debugging and I don't think the issue is the module. The module I am using essentially organizes the information about the context menu and then uses electron.remote functions and a menu.popup function which comes from electron internals. Here is a link to the specific line in github.
const menu = (electron.Menu || electron.remote.Menu).buildFromTemplate(menuTpl);
menu.popup(electron.remote ? electron.remote.getCurrentWindow() : win);
This call to menu.popup leads to this line in electron.
const remoteMemberFunction = function (...args) {
if (this && this.constructor === remoteMemberFunction) {
// Constructor call.
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
return metaToValue(ret)
} else {
// Call member function.
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
return metaToValue(ret)
}
}
So I see a call to ipcRender.sendSync -- however when I add debugging statements in ipcMain's receiver of those calls, I don't see any output!
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
try {
args = unwrapArgs(event.sender, args)
let obj = objectsRegistry.get(id)
if (obj == null) {
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
}
callFunction(event, obj[method], obj, args)
} catch (error) {
event.returnValue = exceptionToMeta(error)
}
})
When I added debug statements to the above function, I didn't see any output. And that is where my search his a wall.
I am using electron 1.4.15. I know this issue should be resolvable, after-all the Atom IDE (which is electron based) does not have this issue even though it has multiple context menus.
I think there is some memory I need to clear somewhere, I just can't figure out how to clear the stack of previous context menus!
I solve this by first getting the target of the click using e.target. Then, depending on that, I call the corresponding contextmenu. If target hit is not in the list of targets for my app, I use a default contextmenu.
window.addEventListener(
"contextmenu",
e => {
e.preventDefault();
if (e.target.id === 'fullscr'){
console.log(e && e.target);
// e.preventDefault();
mymenu.popup(remote.getCurrentWindow());
}else{
editmenu.popup(remote.getCurrentWindow());
}
console.log(e.which);
},
false
);

Orchard CMS Contrib.Review module

I am beginner in Orchard CMS and i need add voting functionality to content. I have installed Contib.Vote and Contrib.Review modules. After that i have added Review part to page content type. Also, i have executed recipe. At the first look everything is fine, but link for review refer to the same page with # symbol and nothing is happenning by clicking on it. It seems like module does not work or work incorrectly. Please help with my problem.
UPD.
Hi devqon and thanx for your help. Your answer was really useful for me. According to your advice i was looking around javascript inside Review Part view file (Parts_Reviews.cshtml). Just for a test i changed its source code a little bit.
#using (Script.Foot())
{
<script type="text/javascript">
//<![CDATA[
(function () {
var numberOfReviewsToShowByDefault = 5;
var $showAllReviewsLink = $('#showAllReviewsLink');
var $deleteReviewConfirmationDialogDiv = $('#deleteReviewConfirmationDialogDiv');
$deleteReviewConfirmationDialogDiv.dialog({ autoOpen: false, modal: true, resizable: false });
$('#deleteReviewLink').click(function () {
$('#reviewId').val($(this).attr("data-review-id"));
ShowDeleteReviewDialog();
return false;
});
$('#showReviewFormLink').click(function () {
$('#createReviewLinkDiv').slideToggle('fast', function () { $('#reviewFormDiv').slideToggle('fast'); });
return false;
});
$('#cancelCreateReviewLink').click(function () {
$('#reviewFormDiv').slideToggle('fast', function() { $('#createReviewLinkDiv').slideToggle('fast'); });
return false;
});
$('#deleteReviewForm').submit(function () {
$('input[type=submit]', this).attr('disabled', 'disabled');
});
$('#cancelDeleteReviewButton').click(function () {
CloseConfirmationDialogDiv();
return false;
});
var rowCount = $('#reviewsList li').length;
if (rowCount > numberOfReviewsToShowByDefault) {
SetupToggle();
}
if (document.location.hash === '#Reviews') {
var topPx = $('#reviews-heading').position().top;
$('body,html').animate({ scrollTop: topPx }, 'slow');
}
if ($("#comment").length) {
var characterCountUpdater = new CharacterCountUpdater($("#comment"), $("#commentCharactersLeft"));
setInterval(function() { characterCountUpdater.UpdateCharacterCount(); }, 100);
$("#comment").keypress(function() { characterCountUpdater.UpdateCharacterCount(); });
if ($("#comment").val().length) {
$("#showReviewFormLink").trigger("click");
}
}
function CharacterCountUpdater(commentBox, charactersLeftBox)
{
this.commentBox = commentBox;
this.charactersLeftBox = charactersLeftBox;
this.maxLength = commentBox.attr("maxlength");
commentBox.removeAttr("maxlength");
return this;
}
Now form for review is displayed. The form looks good, submit button works, character counter works too. But i still can't apply my rating. Stars not react on clicking. That is why submit operation ends with error 'In order to submit a review, you must also submit a rating.'. Look like something inside Parts.Stars.NoAverage.cshtml does not work. Please, help me.
According to the project's site it is a known issue: broken from version 1.7.2.
When looking at the code of the Parts_Reviews.cshtml it says the following on lines 20-24:
string showReviewUri = "#";
if (!Request.IsAuthenticated)
{
showReviewUri = Url.Action("LogOn", "Account", new { area = "Orchard.Users", ReturnUrl = Context.Request.RawUrl });
}
and on line 29:
<div id="createReviewLinkDiv"><span id="createReviewLinkSpan">#noReviewsYetText<a id="showReviewFormLink" href="#showReviewUri">#reviewLinkText</a></span></div>
Therefore, it was intended to let the anchor be # when the request is authenticated (you are logged on). This means it probably will be handled in JavaScript, which can be seen on lines 105-112:
$('#showReviewFormLink').click(function () {
$('#createReviewLinkDiv').slideToggle('fast', function () { $('#reviewFormDiv').slideToggle('fast'); });
return false;
});
$('#cancelCreateReviewLink').click(function () {
$('#reviewFormDiv').slideToggle('fast', function() { $('#createReviewLinkDiv').slideToggle('fast'); });
return false;
});
This piece of code should let you see the form to write a review, so something is going wrong there presumably. When there's something wrong in this jQuery code it probably gives an error in the console, so check out the browser's console when you click the 'Be the first to write a review' link.
This should get you further, if you don't know what to do please provide the error and I will try to dig more. I haven't downloaded the module so I don't have live feed.
Console of Firefox tells: $(...).live is not a function. It refers to Contrib.Stars.js source code file. This function is not supported in jquery now and i replaced it by .on() function in all places api.jquery.com/on. Now module works fine.
Check out my comment at the site below to see how I was was able to get it working again on Orchard 1.8.1:
Orchard Reviews Project Site
You basically just need to change 3 different lines in the Contrib.Stars.js file but I would recommend copying the .js file along with the Review module's different views to a custom theme directory, in order to override everything and force the Reviews module to use your edited .js file:
On line 12 & 13:
Change this:
$(".stars-clear").live(
"click",
To this:
$("body").on(
"click", ".stars-clear",
On line 44 & 45:
Change this:
.live(
"mouseenter",
To this:
.mouseenter(
On line 48 & 49:
Change this:
.live(
"mouseleave",
To this:
.mouseleave(

Resources