How to access Shadow Dom in Intern / Leadfoot - javascript

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');
});

Related

"Uncaught ReferenceError: window is not defined" p5.js web worker

I have a javascript code where I use the web worker with the p5.js library. it wouldn't allow me to use any of p5's functions so I have to use the importScripts("p5.js") function to import the p5.js library before using any of p5's functions.
onmessage = (e)=>{
importScripts("p5.min.js")
// other scripts
}
But even then it gives me another error that said "Uncaught ReferenceError: window is not defined". I tracked it down and it seemed that p5 is unable to use the global variable named "window". I searched around the internet for a solution but so far found none. I wonder if there is a way around this. Thank you.
The issue here is that web workers run in a very isolated context where many of the standard global variables that would exist for javascript running on a website (window, document, etc) don't exist, and unfortunately p5.js cannot load without these variables. You could try shimming them with fake versions. Here's a basic example:
let loadHandlers = [];
window = {
performance: performance,
document: {
hasFocus: () => true,
createElementNS: (ns, elem) => {
console.warn(`p5.js tryied to created a DOM element '${ns}:${elem}`);
// Web Workers don't have a DOM
return {};
}
},
screen: {},
addEventListener: (e, handler) => {
if (e === "load") {
loadHandlers.push(handler);
} else {
console.warn(`p5.js tried to added an event listener for '${e}'`);
}
},
removeEventListener: () => {},
location: {
href: "about:blank",
origin: "null",
protocol: "about:",
host: "",
hostname: "",
port: "",
pathname: "blank",
search: "",
hash: ""
}
};
document = window.document;
screen = window.screen;
// Without a setup function p5.js will not declare global functions
window.setup = () => {
window.noCanvas();
window.noLoop();
};
importScripts("/p5.js");
// Initialize p5.js
for (const handler of loadHandlers) {
handler();
}
postMessage({ color: "green" });
onmessage = msg => {
if (msg.data === "getRandomColor") {
// p5.js places all of its global declarations on window
postMessage({
color: window.random([
"red",
"limegreen",
"blue",
"magenta",
"yellow",
"cyan"
])
});
}
};
This is only going to work for a limited subset of p5.js functions. Any functions that draw to the canvas are definitely not going to work. And I would be cautious about trying to pass objects back and forth (i.e. p5.Vector, p5.Color, etc) because everything sent via postMessage gets serialized and deserialized.
I've posted a working version of this example on Glitch.

Angular2+ e2e testing - Cannot use by.id

I'm new to Angular2 and haven't developed the Angular components to be tested. However, I'm supposed to write some some UI (e2e) tests but I'm not even able to input text in an input field.
My problem is that
element(by.id('username')).sendKeys('test')
is not working. (Same with Button elements and so on)
I'm sure that it is only a small thing but I'm not able to find out what it is.
I have the following configuration:
Proctractor: 5.1.2
chrome driver: 58.0.3029.110
OS: Windows NT 6.1.7601 SP1 x86_64
Spec file:
import { LoginPage } from './login.po';
describe('login tests', function() {
let page: LoginPage;
beforeEach(() => {
page = new LoginPage();
});
it('Demo', () => {
page.navigateTo();
page.getUsernameInput().sendKeys('test');
});
});
Page Object file:
import { browser, element, by } from 'protractor';
export class LoginPage {
navigateTo() {
return browser.get('/login');
}
getParagraphText() {
return element(by.class('app-root h1')).getText();
}
getUsernameInput() {
return element(by.id('username'));
}
}
The HTML template:
....
<div>
<input
id="username"
name="username"
ngModel
type="text"
placeholder="{{something}}"
autocomplete="off"
class="input-text"
required>
</div>
...
Proctractor config
var SpecReporter = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 120000,
getPageTimeout: 120000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
seleniumAddress: 'http://localhost:4444/wd/hub',
baseUrl: 'http://localhost:8001',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
useAllAngular2AppRoots: true,
beforeLaunch: function() {
require('ts-node').register({
project: 'e2e'
});
},
onPrepare: function() {
jasmine.getEnv().addReporter(new SpecReporter());
}
};
Any help is highly appreciated.
EDIT:
None of the solutions worked in my case. I ended up using browser.driver.findElement(by.id('username')); instead of element(by.id('username')); This is unsatisfying because I still don't understand why this doesn't work. I'd be thankful if someone could give me a hint or explanation.
I think your problem is timing.
What happens if you do:
it('Demo', () => {
// wait for page to change to /login
return page.navigateTo().then(() => {
// then look for user input and write 'test'
return page.getUsernameInput().sendKeys('test');
});
});
Edit:
Sounds odd to me that browser.driver.findElement(by.id('username')) works since element(by.id('username')) should be equivalent.
I use a helper class for a lot of the browser interactions, perhaps worth a shot.
Snippets I use for finding element and sending keystrokes:
public static async getElement(locator: By | Function, waitMs?: number): Promise<ElementFinder | any> {
await BrowserHelper.waitForVisibilityOf(locator, waitMs | 1000);
return element(locator);
}
public static sendKeys(locator: By | Function, keys: string, clear?: boolean, waitMs?: number): Promise<void> {
return BrowserHelper.getElement(locator, waitMs).then((element: ElementFinder) => {
if (!clear) {
return element;
}
return element.clear().then(() => element);
}).then((element: ElementFinder) => {
return element.sendKeys(keys)
});
}
public static async waitForVisibilityOf(locator: By | Function, waitMs?: number): Promise<any> {
await browser.wait(EC.presenceOf(element(locator)), waitMs || 5000).then(() => {
// visible
}, (error) => {
console.error('timeout at waitForVisibilityOf', locator, error);
});
}
I believe this is due to your getUsernameInput() method returning not the locator in this case. As per Protractor documentation,
The element() function returns an ElementFinder object. The ElementFinder knows how to locate the DOM element using the locator you passed in as a parameter, but it has not actually done so yet. It will not contact the browser until an action method has been called.
You can try this modified code
getUsernameInput() {
element(by.id('username')).sendKeys('text');
}
}
and then using
it('Demo', () => {
page.navigateTo();
page.getUsernameInput();
});
});
Also, I'm not sure your getText() would return the text, because getText() returns a Promise, which you would have to resolve. This has been explained here.

nightwatch.js assert on multiple page sections

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;
}

Polymer dom-repeat issue

While rendering with Polymer an array of objects, it keeps launching me an exception.
Here's the data model retrieved from server:
{
"lastUpdate":"yyyy-MM-ddTHH:mm:ss.mmm",
"info": [
{
"title": "Some nice title"
},
...
]
}
Here's my Polymer component template:
<dom-module is="items-list">
<template>
<dl>
<dt>last update:</dt>
<dd>[[$response.lastUpdate]]</dd>
<dt>total items:</dt>
<dd>[[$response.info.length]]</dd>
</dl>
<template is="dom-repeat" items="{{$response.info}}">
{{index}}: {{item.title}}
</template>
</template>
<script src="controller.js"></script>
</dom-module>
And here's the controller:
'use strict';
Polymer(
{
properties: {
info: {
type: Array
},
$response: {
type: Object,
observer: '_gotResponse'
}
},
_gotResponse: function(response)
{
console.log(response);
if (response.info.length)
{
try
{
//here I try to set info value
}
catch(e)
{
console.error(e);
}
}
},
ready: function()
{
//set some default value for info
},
attached: function()
{
//here I request the service for the info
}
}
);
If tried to set info value as:
this.info = response.info;
this.set('info', response.info);
this.push('info', response.info[i]); //inside a loop
But the result breaks after rendering the first item, the exception launched is:
"Uncaught TypeError: Cannot read property 'value' of null"
If info === $response.info then why not just use items={{info}} in the repeat?
edit:
Try to modify your code in the following way.
'use strict';
Polymer({
properties: {
$response: {
type: Object
}
},
_gotResponse: function(response)
{
console.log(response);
...
this.$response = response
...
},
attached: function()
{
//here I request the service for the info
someAjaxFn(someParam, res => this._gotResponse(res));
}
});
You don't need an observer in this case. You only use an explicit observer when the implicit ones don't/won't work. I.e. fullName:{type:String, observer:_getFirstLastName}
or
value:{type:String, observer:_storeOldVar}
...
_storeOldVar(newValue, oldValue) {
this.oldValue = oldValue;
}
if you are updating the entire array, ie this.info, then you simple use this.info = whatever once within your function. If you don't want to update the entire array, just some element within it, then you will want to use polymer's native array mutation methods as JS array method doesn't trigger the observer.
Again, since your template doesn't use info, then you don't need the property info. If you want to keep info, then don't store info within $response. In fact, $ has special meaning in polymer so try not to name properties with it. you can simply use the property info and lastUpdate for your polymer.
Last note, beware of variable scoping when you invoke functions from polymer. Since functions within polymer instances often use this to refer to itself, it may cause
Uncaught TypeError: Cannot read property 'value' ..."
as this no longer refers to the polymer instance at the time of resolve.
For example, suppose your host html is
...
<items-list id='iList'></items-list>
<script>
iList._gotResponse(json); // this will work.
setTimeout(() => iList.gotResponse(json),0); //this will also work.
function wrap (fn) {fn(json)}
wrap (iList._gotResponse); //TypeError: Cannot read property '$response' of undefined.
function wrap2(that, fn) {fn.bind(that)(json)}
wrap2 (iList,iList._gotResponse); // OK!
wrap (p=>iList._gotResponse(p)) //OK too!!
wrap (function (p) {iList._gotResponse(p)}) //OK, I think you got the idea.
</script>

Why doesn't the Reflux.js listenAndPromise helper work?

I'm using qwest to query my endpoint as shown below, the onGetResourceCompleted handler fires as expected but data is undefined. Why?
var Actions = Reflux.createActions({
'getResource': { asyncResult: true }
});
Actions.getResource.listenAndPromise(function (id) {
return qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true });
});
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('');
},
onGetResourceCompleted: function (data) {
console.log('OK', data); // Get's called but data is undefined. Why?
}
});
I can see the data loads correctly by looking at dev tools as well as calling qwest in isolation by simply doing:
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true }).then(function(data) {
console.log('OK', data);
});
Also doing the following works:
ServiceActions.getResource.listen(function (id) {
ServiceActions.getResource.promise(
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true })
);
});
I've put some comments on the cause of this "confirmed bug" in the original issue you opened at github.com/spoike/refluxjs.
So, though you are using the reflux features the way they are intended, and they're definitely creating a race condition without even returning the race results, I think you're in luck. It turns out the two particular features you're using in this combination with this type of request is a bit redundant when you already have a promise available. I'd recommend you just drop the onGetRequestCompleted handler entirely, and handle completion using the standard promise ways of handling resolved promises, which honestly will give you more flexibility anyways.
For example:
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('')
.then() <-- this eliminates the need for onGetResourceCompleted
.catch() <-- or this instead/in addition
.finally() <-- or this instead/in additon
},
// no more onGetResourceCompleted
});

Categories

Resources