how to find href attribute to an element using cypress testing library? - automated-tests

using findByRole I get this error because of multiple matches on search my parameter.
Timed out retrying after 5000ms: Found multiple elements with the role "link" and name /eligibility/i
Here are the matching elements:
<a
href="/HHIN/PBEligibility/EligibilitySearch.aspx"
>
Eligibility
</a>
<a
href="/HHIN/RiderEligibility/EligibilitySearch.aspx"
>
Rider Eligibility
</a>
<a
href="/HHIN/QUESTEligibility/EligibilitySearch.aspx"
>
Eligibility
</a>
<a
href="/HHIN/BXEligibility/EligibilitySearch.aspx"
>
Eligibility
</a>
(If this is intentional, then use the AllBy variant of the query (like queryAllByText, getAllByText, or findAllByText)).
using findAllByRole then within method returns all 5 elements, but then using the findByText method doesn't single out the element i need(pbeligibility). How can I query by href attribute to find that unique value string 'pbeligibility'? or any other way to click on that first eligibility index? I know how to do it using native cypress methods but I want to do it using the testing library methods.
it('this will login to HHIN', () => {
cy.log('Opening HHIN Login Page');
cy.visit("URL");
cy.log('Entering in Username/Password');
cy.findByRole('row', { name: /User/i }).should('exist');
cy.findByRole('textbox', { input: /UserId/i }).type('username');
cy.findAllByRole('row', { name: /password/i }).type('password');
cy.findByRole('button', { name: /login/i }).click();
cy.get('body').then((body) => {
if (body.find('input#ctl00_cphContent_btContinue').length > 0) {
cy.get('input#ctl00_cphContent_btContinue').click();
} else {
cy.title().should('eql', 'HHIN');
}
cy.findAllByRole('link', { name: /eligibility/i }).within(() => {
cy.findByText(/PBelegibility/i).click();
});
});
// const pbEligObj = cy.findAllByRole('link', { name: /eligibility/i });
// cy.log(pbEligObj);
// for (let key in pbEligObj) {
// if (pbEligObj.hasOwnProperty(key)) {
// let value = pbEligObj[key];
// cy.log(key, value);
// }
// }
});
});

Related

Meteor js custom pagination

I kindly need a support. I have written a pagination following a YouTube tutorial, which works fine with the exception of when going backwards again. It has only 2 buttons, previous and next, when the next button is clicked it works fine but the previous button only go backwards once. Let's say I have 20 records in the collections paginated to display 5 at a time, the next button could go to the end to the fourth page but the previous button would not go pass one step backwards. Please what am I to do to have pagination experience? The previous button is suppose to navigate to the last page as long as the user clicks.
Events for the pagination buttons:
Template.myviews.events({
'click .previous': function () {
if (Session.get('skip') > 5 ) {
Session.set('skip', Session.get('skip') - 5 );
}
},
'click .next': function () {
Session.set('skip', Session.get('skip') + 5 );
}
});
Publication:
Meteor.publish('userSchools', function (skipCount) {
check(skipCount, Number);
user = Meteor.users.findOne({ _id: this.userId });
if(user) {
if(user.emails[0].verified) {
return SchoolDb.find({userId: Meteor.userId()}, {limit: 5, skip: skipCount});
} else {
throw new Meteor.Error('Not authorized');
return false;
}
}
});
Subscription:
Session.setDefault('skip', 0);
Tracker.autorun(function () {
Meteor.subscribe('userSchools', Session.get('skip'));
});
Blaze pagination buttons:
<ul class="pager">
<li class="previous">Previous </li>
<li class="next">Next </li>
</ul>
Template helper:
RenderSchool: function () {
if(Meteor.userId()) {
if(Meteor.user().emails[0].verified) {
return SchoolDb.find({userId: Meteor.userId()}).fetch().reverse();
} else {
FlowRouter.go('/');
Bert.alert('Please verify your account to proceed', 'success', 'growl-top-right');
}
}
}
You have 6 documents total with 2 docs per page, 3 pages in total.
Your if condition in previous button click handler prevents you from going to the first page:
if (Session.get('skip') > 2 /* testing */ ) {
...
}
For 2nd page skip will equal 2 and on the next click this condition will be false, preventing from going back.
When you're on the 3rd page — you can go on 2nd only, creating an impression that button works only once.
It should be like this:
if (Session.get('skip') > 0 ) {
...
}

Search and Sort on the same page

I'm trying to implement sort and search to my items, so i started with sort and it works:
Template
<button class="sort">Sort</button>
{{#each cvs}}
{{> Interviu}}
{{/each}}
JS:
Template.Interviuri.onCreated(function () {
var self = this
self.autorun(function () {
self.sortOrder = new ReactiveVar(-1)
})
Template.Interviuri.helpers({
cvs() {
const instance = Template.instance()
return Cvs.find({}, { sort: { createdAt: instance.sortOrder.get() } })
},
})
Template.Interviuri.events({
'click .sort'(event, instance) {
instance.sortOrder.set(instance.sortOrder.get() * -1)
Next i wanted to implement Search on the same page. So the best way i could found was EasySearch.
But using EasySearch, it means i must change the way my items are being displayed. And then the sort doesn't work anymore.
Template
<div class="searchBox pull-right">
{{> EasySearch.Input index=cvsIndex attributes=searchAttributes }}
</div>
{{#EasySearch.Each index=cvsIndex }}
{{> Interviu}}
{{/EasySearch.Each}}
Collection
CvsIndex = new EasySearch.Index({
collection: Cvs,
fields: ['name'],
engine: new EasySearch.Minimongo()
})
JS
cvsIndex: () => CvsIndex,
How can i have both search and sort working at the same time?
With EasySearch you can use two methods on your index, namely getComponentDict() and getComponentMethods().
With getComponentDict() you can access search definition and options:
index.getComponentDict().get('searchDefinition');
index.getComponentDict().get('searchOptions');
You also have the corresponding setters to change the search definition/option.
getComponentMethods has mehods like
index.getComponentMethods().loadMore(integer);
index.getComponentMethods().hasMoreDocuments();
index.getComponentMethods().addProps(prop, value);
index.getComponentMethods().removeProps([prop])
From that you can set your prop, say index.getComponentMethods().addProp('sort', -1) and then on the index definition, in your MongoDB engine, set the sort from that prop:
index = new EasySearch.index({
// other parameters
engine: new EasySearch.MongoDB({
sort: function(searchObject, options) {
if(options.search.props.sort) {
return parseInt(options.search.props.sort);
}
return 1;
}
})
});
See EasySearch Engines for more info.

"This operation is insecure" when using class properties as key

I have a custom FileArgument class that I'm using to store information about an uploaded file:
export class FileArgument {
name: string;
path: string;
file: File;
}
My upload works fine and the server then returns the path where the file was uploaded. I then want to store this path in a dictionary using a previously set fileArgument.name as key. Below is a simplified overview of my component. onSubmit() is where the action is happening:
export class InputModuleComponent {
private vsmodule: InputModule;
private moduleArguments = {};
private fileArgument: FileArgument = new FileArgument();
#Input()
module: InputModule;
constructor(private inputModuleService: InputModuleService) {}
onSubmit(): void {
this.inputModuleService.postFile(this.fileArgument.file).subscribe(
response => {
this.moduleArguments[this.fileArgument.name] = response.filename;
console.log(this.moduleArguments);
},
error => {}
);
}
onFileChange(event): void {
this.fileArgument.file = event.originalTarget.files[0];
this.fileArgument.name = event.originalTarget.id;
}
}
Line 14 above (this.moduleArguments[this.fileArgument.name] = response.filename;) causes the following error in Firefox:
EXCEPTION: Uncaught (in promise): SecurityError: The operation is insecure.
and in Chrome:
core.umd.js:5995 EXCEPTION: Uncaught (in promise): InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
If I replace that line with, for example:
this.moduleArguments['hello'] = response.filename;
I don't get any errors. The error clearly comes from using fileArgument.name as a dict key, but I have no idea why.
EDIT: The postFile() method from my service is below:
postFile (file: File): Observable<any> {
console.log('input-module.service - postFile()');
var url = this.uploadURL;
return Observable.create(observer => {
var formData: FormData = new FormData()
var xhr: XMLHttpRequest = new XMLHttpRequest();
formData.append("upload", file, file.name);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
observer.next(JSON.parse(xhr.response));
observer.complete();
} else {
observer.error(xhr.response);
}
}
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
The component HTML:
<a (click)="modal.open()">
{{vsmodule.displayName}}
</a>
<modal #modal>
<form (ngSubmit)="onSubmit()">
<modal-header [show-close]="true">
<h4 class="modal-title">Input Module - {{vsmodule.displayName}}</h4>
</modal-header>
<modal-body>
<p>{{vsmodule.description}}</p>
<hr>
<ul>
<li *ngFor="let arg of vsmodule.args; let i = index">
<fieldset *ngIf="arg.type == 'file'">
<label>{{ arg.displayName }}</label>
<input
name="{{arg.name}}"
id="{{ arg.name }}"
type="file"
[(ngModel)]="moduleArguments[arg.name]"
(change)="onFileChange($event)"
>
<p>{{ arg.description }}<p>
</fieldset>
</li>
</ul>
</modal-body>
<modal-footer>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">Dismiss</button>
<button type="submit" class="btn btn-primary">Run</button>
</modal-footer>
</form>
</modal>
In onChange, fileArgument.name is set to the value of event.originalTarget.id - the id of an actual HTML element in the page
And chrome error is saying:
Failed to set the 'value' property on 'HTMLInputElement'
Edit since you added the html - you have bound the 'moduleArguements' property to the file input element's ngmodel - as a result, changing that value will cause angular to try and modify the value property on the file input which is not permitted.
What is the purpose of updating that value? Is it just to feedback to the user?
If you remove the ngModel binding from the input element it should work - you are using the onFileChange event to capture the new filename anyway (although in the controller it is just onChange?)
Short Answer: You cannot cannot actually change the value of the this.moduleArguments[this.fileArgument.name]as it would be a security issue.
Explaination: You would be changing the actual value of this.fileArgument.name to something else, which would allow other people with ill intent to do the same. Essentially, an attacker could change that name to redirect any attempts to use that file to another file. So, Java (or Flash, or any other programming language) could not programmatically change that due to security reasons.
With your work-around, you are not actually setting a File data member, therefore JS does not see this as a security hazard.
Remember, [almost] anything you can do involving website/server code (or code interacting with either), an attacker can do as well. That's why JS, in this case, blocks people from changing the content of this specific standard File object. Hope this helps!
have you tried this:
var fileName = this.fileArgument.name;
this.moduleArguments[fileName] = response.filename;
and also if you are somewhere in JS changing the 'value' of your
tag, you will get that error, please refer to:
https://stackoverflow.com/a/29721020/2071008

trying to render a list. nothing showing on screen

Using Meteor and React. Trying to render a list of data from the server onto the client. the server's data looks like this:
Searches.insert({text: data.items[i].snippet.title});
if(Meteor.isClient) {
Searches = new Meteor.Collection('searches');
Meteor.subscribe('allSearches');
}
....
renderTasks(){
return this.data.searches.map((searches) => {
return <SearchResultItem searches={searches} />;
});
},
....
<ul>
{this.renderTasks()}
</ul>
....
SearchResultItem = React.createClass({
render(){
return
<li>
{this.props.searches.text}
</li>
}
});
You need to provide an unique key-prop to your dynamic child elements
Dynamic Children
The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key:
render: function() {
var results = this.props.results;
return (
<ol>
{results.map(function(result) {
return <li key={result.id}>{result.text}</li>;
})}
</ol>
);
}
(REF: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children)
In your case:
renderTasks(){
return this.data.searches.map((searches, i) => {
return <SearchResultItem key={i} searches={searches} />;
});
}

Meteor retrieving document _id from reactive table

I am using Reactive-Table to display data saved in my Meteor app as shown from the code below, in each row of the table there is a link to edit the document related to this row. I am trying using the edit link 'click event' to capture the _id of the document related to the row being selected but can't seem to get the _id, can someone please check my code and let me know what I am missing / doing wrong here and how to capture the _id? Thanks
customerslist.html
<template name="customerslist">
<div class="customerslist">
<div class="page-header">
<h1>Customers List</h1>
</div>
<div>
{{> reactiveTable class="table table-bordered table-hover" collection=customersCollection settings=settings}}
</div>
</div>
</template>
customerslist.js
Template.customerslist.helpers({
customersCollection: function () {
return Customers.find({},{sort: {title: 1}});
},
settings: function () {
return {
rowsPerPage: 10,
showFilter: true,
showColumnToggles: false,
fields: [
{ key: 'name', label: 'Customer Name' },
{ key: 'email', label: 'Email' },
{ key: 'phone', label: 'Phone' },
{ key: '_id', label: 'Action', sortByValue: false, fn: function(_id){ return new Spacebars.SafeString('<a name="' + _id +'" class="edtlnk" target="_blank" href="' + _id + '/edit"> Edit </a>'); } }
]
};
}
});
Template.customerslist.customers = function () {
return Customers.find({},{sort: {title: 1}});
};
Template.customerslist.events({
'click .edtlnk': function(e) {
var cust = this;
event.preventDefault();
console.log('Customer ID: '+cust._id);
}
});
The way the package sets up data contexts, this will only be set to the customer object if the event selector matches the tr element. That makes event.currentTarget the tr, but event.target is still the edit link.
You could try something like this:
Template.customerslist.events({
'click .customerslist tr': function(e) {
if ($(e.target).hasClass('edtlnk')) {
var cust = this;
e.preventDefault();
console.log('Customer ID: '+cust._id);
}
}
});
I don't know Meteor though I am starting to play around with it so I don't care about up or down votes at all but learning the answer your question.
I found the Event Maps docs which I am sure you saw as well:
https://docs.meteor.com/#/full/eventmaps
This was listed in the doc:
{
'click p': function (event) {
var paragraph = event.currentTarget; // always a P
var clickedElement = event.target; // could be the P or a child element
}
}
If a selector matches multiple elements that an event bubbles to, it will be called multiple times, for example in the case of 'click
div' or 'click *'. If no selector is given, the handler will only be called once, on the original target element.
The following properties and methods are available on the event object passed to handlers:
type String
The event's type, such as "click", "blur" or "keypress".
target DOM Element
The element that originated the event.
currentTarget DOM Element
The element currently handling the event. This is the element that matched the selector in the event map. For events that bubble, it may be target or an ancestor of target, and its value changes as the event bubbles.
It seems to me that since target and currentTarget are DOM elements can't you get what you need from those or are you referring to the _id available in Meteor on an insert callback?

Resources