I am working with nightwatch.js
i have a page file that looks like this:
sections: {
table: {
selector: '.sr-filterable-data-layout--collection',
elements: {
header: {
selector: '.sr-collection--header'
},
body: {
selector: 'sr-collection--body'
}
}
},
filters: {
selector: '.sr-filterable-data-layout--filters',
elements: {
filterByName: {
selector: '#filter-name'
}
}
},
actions: {
selector: '.sr-entities-actions',
elements: {
addButton: {
selector: '.mdi-content-add'
}
}
}
},
commands: [{
editEntity(options) {
return this.section.body();
},
verifyPageload() {
return (
this.section.filters.waitForElementVisible('#filterByName')
.section.table.waitForElementVisible('#header')
// .section.actions.waitForElementVisible('#addButton')
.assert.title(this.props.title)
);
}
}
]
asserting on each of the elements individually works but when i try to chain the assertions like this:
this.section.filters.waitForElementVisible('#filterByName')
.section.table.waitForElementVisible('#header')
it fails with the following error:
✖ TypeError: Cannot read property 'waitForElementVisible' of undefined
any help regarding how to chain these asserts together will be much appreciated
You cannot do that, as section is a page property, while waitForElementVisible returns a reference to the client instance ("browser"), not to the page.
Just split the commands, there's really no reason to chain them.
Another thing; the return () block is redundant here, just return the assertion result directly:
verifyPageload() {
// waitForStuff...
return this.assert.title(this.props.title)
}
while the comments here suggest ways to circumvent the issue. i finally stumbled upon the way to chain multiple actions on different sections by chaining .parent after the first call to return the root of the page and not the section like this:
verifyPageload() {
this.section.table
.waitForElementVisible('#header')
.parent.section.filters.waitForElementVisible('#filterByName')
.parent.section.actions.waitForElementVisible('#addButton')
.assert.title(this.props.title);
return this;
}
Related
I am adding a new page to a website, and I am copying the code that already exists and is currently working in the website. Why is the FlowRouter.getParam coming up undefined when it works everywhere else?
client/JobInvoice.js
import { Invoices } from '../../../imports/api/Invoice/Invoice';
Template.InvoicePage.onCreated(function(){
const user = FlowRouter.getParam('_id');
console.log(user);
this.subscribe('invoices', user);
});
lib/router.js
Accounts.onLogout(function(){
FlowRouter.go('home');
});
FlowRouter.notFound = {
action: function() {
FlowRouter.go('/404');
}
};
const loggedIn = FlowRouter.group({
prefix: '/secure'
});
loggedIn.route( '/invoice', {
name: 'invoice',
action() {
BlazeLayout.render('FullWithHeader', {main:
'InvoicePage'});
}
});
What am I missing?
FlowRouter allows you to define routes with dynamic attributes (path-to-regexp), which are often representing document ids or other dynamic attributes.
For example
FlowRouter.route('/invoice/:docId', { ... })
would define a route that matches a pattern like /invoice/9a23bf3uiui3big and you usually use it to render templates for single documents.
Now if you want to access the document id as param docId inside the corresponding Template you would use FlowRouter.getParam('docId') and it would return for the above route 9a23bf3uiui3big.
Since your route definitions lacks a dynamic property, there is no param to be received by FlowRouter.getParam.
A possible fix would be
loggedIn.route( '/invoice/:_id', {
name: 'invoice',
action() {
BlazeLayout.render('FullWithHeader', {main:
'InvoicePage'});
}
});
to access it the same way you do for the other templates.
Readings
https://github.com/kadirahq/flow-router#flowroutergetparamparamname
Here is what I ended up doing and it works.
loggedIn.route( '/invoice/:id', {
name: 'invoice',
action() {
BlazeLayout.render('FullWithHeader', {main: 'InvoicePage'});
}
});
I have a component that has a a button that only shows when an article.link prop is not empty. I want to write a test to check the button is rendered when the article.link is not empty and another one for when it is empty
my component looks like so :
a.btn.plutus_btn-primary.round( v-if="hasArticleLink" target='_blank' :href="articleLink") Start Shopping Now
hasArticleLink is a computed property that return true when the link is not empty.
The unit test I wrote looks like this :
it("should not renders the link button when article doesn't have a link", () => {
wrapper = mount(MerchandisingArticle, {
propsData: {
article: {
link: ""
}
}
});
expect(wrapper.find("a").exists()).toBe(false);
});
it("renders the linked button when article has link", () => {
wrapper = mount(MerchandisingArticle, {
propsData: {
article: {
link: "https://google.com"
}
}
});
expect(wrapper.find("a").exists()).toBe(true);
});
that works just fine but I was wondering if there is a better way to test these reversed cases as I think this one is kinda duplicated since I have to mount the component in every it bloc ? Any help is appreciated!
I Think it's good to mount the component before every test, because this way there will be no sideeffects to a new test from a previous one.
But you could do something like this to not duplicate code:
const expectedValues = [
{ link: '', expects: false },
{ link: 'www.google.com', expects: true },
....
]
expectedValues.forEach(({ link, expects }) => {
it(`should evaluate ${expects} for link:${link} `, () => {
const wrapper = mount(MerchandisingArticle, {
propsData: {
article: {
link
}
}
})
expect(wrapper.find("a").exists()).to.equal(expects);
})
})
This way you can also easily add new test cases.
I have a problem with implementing Page Object in Nightwatch. Let's say that I have a login scenario. I need to scroll to the element - I'm using for thar execute function.
module.exports = {
'Login' : function (browser) {
browser.url(this.launchUrl)
.setValue('input[name=username]', 'admin')
.setValue('input[name=password]', 'password')
.execute(function () {
document.querySelector('input[type=submit]').scrollIntoView();
}, [])
.click('input[type=submit]');
browser.end();
}
}
I'd like to refactor this login code into Page Object like that
module.exports = {
url: function() {
return this.api.launchUrl;
},
commands: [scrolling],
elements: {
usernameField: {
selector: 'input[name=username]'
},
passwordField: {
selector: 'input[name=password]'
},
submit: {
selector: 'input[type=submit]'
}
}
};
I'd like to 'hide' also this execute command and pack it into commands, like that:
var scrolling = {
scroll: function(){
return this.execute(function () {
document.querySelector(input[type=submit]').scrollIntoView();
}, []);
}
};
Unfortunately it seems that execute command doesn't work with Page Object.
How I can overcome this issue with executing JavaScript code when I want to use Page Object? How can I encapsulate it?
The answer was very simple
1) There was a quotation mark missing in a selector.
2) Using execute() in Object Pattern it is needed to run it using this.api :
this.api.execute(function () {
document.querySelector('input[type=submit]').scrollIntoView();
}, []);
Found the answer
ForthStCheck:function(){
this.api.execute('scrollTo(0,500)')
this.waitForElementVisible('#forthStationPlayBtn',5000)
}
I'm trying to do functional tests for a Google Polymer project using InternJS.
The Web-Components part looks like the following:
<custom-element-one flex>
<custom-nested-element id="someId">
</custom-nested-element>
</custom-element-one>
The problem is that I can not access the Shadow DOM within the tests:
return this.remote
.get(require.toUrl('http://localhost:8500/test.html'))
.then(pollUntil('return document.querySelector("custom-element-one").shadowRoot;', 20000))
.findByTagName('custom-element-one')
.getProperty('shadowRoot')
.then(function (doc) {
console.log('1--------------------->>>>', doc);
console.log('2--------------------->>>>', doc.findByTagName('custom-nested-element'));
doc.findByTagName('custom-nested-element')
.getAttribute('id')
.then(function (doc) {
console.log('3--------------------->>>>', doc);
});
});
Results:
First log returns the following:
1--------------------->>>> { _elementId: '8',
_session:
{ _sessionId: 'xxxx-xxxx-xxx',
_server:
{ url: 'http://localhost:4444/wd/hub/',
sessionConstructor: [Function: ProxiedSession] },
_capabilities:
{ applicationCacheEnabled: false, ...
2--------------------->>>> { cancel: [Function], then: [Function] }
Object #<Promise> has no method 'getAttribute'
Any suggestion is appreciated.
My guess is that shadowRoot is not part of the leadFoot library yet and it is not possible to access the shadow DOM on nested
This is mostly the WebDriver issue rather than anything else. Support of Shadow DOM is very limited in WebDriver. (more).
But as a work around this, you could use pollUntil to grab the element and then get any of its attributes or call any of its exposed methods.
It would be similar to this, if you want to test value of the id attribute:
return this.remote
.get(require.toUrl('http://localhost:8500/test.html'))
.then(pollUntil(function () {
if (document.querySelector('custom-element-one').shadowRoot) {
return document.querySelector('custom-element-one').shadowRoot.querySelector('custom-nested-element').id;
}
return null;
}, [] , 20000))
.then(function (idValue) {
assert.equal(idValue, 'someId');
});
normally i would use this to load dependency
main: function() {
require(['views/home'], function(HomeView) {
_pageView.render(HomeView);
});
}
but now am looking of simplifying it by doing this
main: function() {
require(['views/home'], this.homeView);
},
homeView: function(HomeView) {
this.page = _pageView.render(HomeView);
}
but the keyword this is unrecognizable. How to make it recognizable.
Calling require like this:
require(['views/home'], this.homeView.bind(this));
should prevent this from getting set to a different value when RequireJS calls the callback.