Line within render() :
<div class={{ 'modal-backdrop show': this.show, 'modal-backdrop hide': !this.show }}>
Test :
it('should display correctly', async () => {
const page = await newE2EPage();
await page.setContent('<my-component></my-component>');
let element = await page.find('my-component');
expect(element).not.toBeNull();
element = await page.find('div.modal-backdrop.hide');
expect(element).not.toBeNull();
});
Description of the issue:
I have provided only a part of the code and unfortunately I cannot provide much more due to confidentiality. However I will do my best to describe the issue. There are two components in the project, tests for component A work as they should. Tests for component B (provided above) do not. While the first expect passes, the second one fails due to it being null but it shouldn't.
A few facts:
The project can be built, run and used without a problem.
Unit tests work as intended, including tests for the render() method.
The code in it-self is not wrong, I have tested, retested and tested again and it works for other components but not for this one.
Although the default is .hide, I have tried with both .hide and .show, neither work.
Best guess so far:
I have had many issues getting the tests to work due to how the code is written. While running tests many objects where undefined and that was causing the tests to fail. From everything that I tried and tested my best theory is that for some reason this component half fails in the context of the puppeteer browser, making the core object but nothing else. I don't know if that is possible but it looks like that.
Web components use their own document tree called shadowDOM, which isn't visible from the main DOM (page); thus your page.find fails. This concept is called encapsulation. Btw, I wasn't able to find a method called find on the page object in Puppeteer's documentation; can you explain where it comes from?
To access the shadow tree inside a web component, you'll have to access it using element.shadowRoot:
element = await page.find('my-component');
expect(element.shadowRoot.querySelector('div.modal-backdrop.hide')).not.toBeNull();
There's puppeteer addons and applications which can help with that:
https://github.com/PavelDymkov/puppeteer-shadow-selector
https://docs.puppetry.app/testing-techniques/testing-shadow-dom
To find more, check https://www.google.com/search?q=puppeteer+shadow+DOM.
Related
We are currently making the client retrieve the object states when the page loads (which will cause the 'pending' objects in the model to turn into different colors). Then we poll for changes to update the coloring (Firstly: pending object gets colored when the viewer loads, and then we keep polling to check and change state again, to make Forge render those in a different color and store their old color/material. When the polling received a change that an object should no longer be colored, it tells Forge to use the old color/material again.
The problem:
We've found out what the problem is, but we couldn't find out how to fix it. The problem is that changing materials in Forge doesn't work after startup anymore, it only works in the first ~3 seconds or so (the materials were used to show the colors).
However, setting overlays works even after the first ~3 seconds, (showing overlays instead of materials to show the colors).
This is not what we want to achieve. This looks unoptimized, because overlays will be shown through everything.
The materials, however, seem to be 'locked', as in, they cannot be changed anymore after the first ~3 seconds. It seems like they aren't refreshed or something
In the examples, we found they used viewer.impl.invalidate(true) to refresh the Forge viewer, but that doesn't do anything after ~3 seconds.
We've also tried every combination of viewer.impl.invalidate(true, true, true) as well as setting material.needsUpdate to true, as well as trying to re-render the entire scene.
We also found this: https://github.com/mrdoob/three.js/issues/790, but we couldn't find a good way to do that in Forge, we tried viewer.requestSilentRender() but that didn't do anything either.
Anyway, we've tried everything we could come up with and could find online to make the materials work, but nothing made a difference.
We are looking to find someone that's more experienced with how Forge works that can see what the material code is doing wrong.
As for the content, here is all the code you will need to understand what is happening:
DROPBOX LINK
And here is a small part of the "index.html" file that sets the color:
try
{
viewer.restoreAllColorOverlays(); //for materials instead of overlays: viewer.restoreAllColorMaterials();
$.each(colors, function(color, selectionIds)
{
viewer.setColorOverlay(selectionIds, color); //for materials instead of overlays: viewer.setColorMaterial(selectionIds, color);
});
}
catch(error)
{
console.error(error);
}
I have no idea how you implement your app, so I only tell what I found in your codes. If you want to resolve the issue you addressed, you can consider providing a reproducible case demonstrating that, I will gladly pass it to our dev team. Those following items should be in the reproducible case:
A short exact description of what you are trying to achieve. The behavior you observe versus what you expect, and why this is a problem.
A complete yet minimal sample source model to run a test in.
A complete yet minimal Forge app that can be run and debugged with a simple procedure to analyze its behavior lives in the sample model.
A complete yet minimal pure three.js app that can be run and demonstrated the shader effect you want. Note. Forge Viewer is using r71 three.js.
Detailed step-by-step instructions for reproducing the issue, e.g. which element to pick, what command to launch etc.
If your reproducible case could not be posted here publicly, please send it to the forge.help#autodesk.com and remove sensitive data or information before you send.
=== Something I found in your codes:
I found here are some wrong types and missing actions in your ColorMaterial extension. The color property of an material should the a type of the THREE.Color. Here is my modification:
Autodesk.Viewing.Viewer3D.prototype.setColorMaterial = function(objectIds, color)
{
if( !(color instanceof THREE.Color) ) throw 'Invalid argument: Color';
var material = new THREE.MeshPhongMaterial
({
color: color,
opacity: 0.8,
transparent: true
});
viewer.impl.matman().addMaterial( 'ColorMaterial-' + new Date().getTime(), material, true );
// ...........
};
Its' result is here:
In the ColorOverlay extension, The type of material color property is also wrong, it should be a type of THREE.Color, too. Changing it into THREE.Color should work fine. In addition, overlay is covers on 3D objects, so you should call viewer.hide() with your setColorOverlay() together. Otherwise, it won't look like a transparent object.
Without hidding 3D object of the wall:
hide 3D object of the wall:
I'm a bit amazed I haven't been able to find an explanation for how to do this as it seems like it's fairly elemental to debugging, but I can't find anywhere how to print the attributes of an object in Polymer.
I'm learning Polymer and I keep running into situations where I have an object, but I have no idea what the attributes are of the object. (Ex. I print to the window, and I get [object Object]. I've found some explanations for how to print a list of the keys/attributes of an object (I know how to print the values for those keys if I know what they are), but I have no idea how to get the keys if I don't already know the format of my data. Every example presumes you already know what the attributes are.
I've seen solutions recommending adding a script like:
getKeys : function(o){
return Object.keys(o);
}
And then they recommend something like this:
<template is="dom-repeat" items="{{ item in obj | getKeys}}">
{{item}}
</template>
But I think they must work off maybe an earlier version of polymer. Most are from 2014-ish and I know the library has changed a lot since then.
This is the closest thing I get to an error with this code:
Polymer::Attributes: couldn`t decode Array as JSON
Here's an example post recommending this strategy. I understand I could dig deeper into the documentation and try to understand what response is supposed to be coming back, but I'm more curious what the general strategy is for this situation - I've multiple times wanted to check to see how polymer was modeling something vs how I thought it was.
The post you mention recommends a method that is no longer possible with post-1.0 Polymer, which does not support that syntax of filtering/pipes (as of the current release, 1.5.0).
You could use DevTools to select the Polymer element and then run console.dir($0). This works in the following browsers (and maybe older versions):
Chrome 50
Firefox 45
Safari 9.1
Opera 39
Chrome and Opera display all keys (even inherited ones from HTMLElement) in sorted order, so it can be tedious to scan through the long list of keys for a Polymer-specific property. However, Firefox and Safari list Polymer-specific keys first and then the inherited ones.
One workaround for Chrome/Opera is to use this snippet:
((o) => {
let obj = {};
Object.keys(o).sort().forEach((x) => {
obj[x] = o[x];
});
console.dir(obj);
})($0);
Here's a codepen that logs the attributes of a paper-button. You don't need to click the button. Open the browser's console log (not the Codepen console) to see something like the screenshot below. You can expand the fields in the console log to see the attributes of the Polymer object.
The solution I have been using is the following:
Place a button somewhere on the visible page.
When that button is tapped, print the object to the console.
my-element.html
<button on-tap="show">Click here to see user</button>
...
show: function() {
console.log('user', this.user);
},
...
You can also use console.dir() as follows.
<my-element id="foo"></my-element>
...
bar: function() {
console.dir( this.$.foo );
}
I'm a little stumped on this one. I defined a route called classes/:id. When navigating to that route in the app, the componentDidMount() is called. However, when reloading the page or copying and pasting the URL the page completely loads, but the componentDidMount() method is not called at all.
From what I have read, the reason is because the same component mounted even though the page is being reloaded which is why the lifecycle method does ever get fired off.
What are some ways to handle this? Your help is greatly appreciated. Thanks!
componentDidMount will not be called again if you are already at a classes/:id route. You'll probably want to do something along the lines of:
componentDidUpdate(prevProps) {
if (this.props.id !== prevProps.id) {
// fetch or other component tasks necessary for rendering
}
}
I try to avoid any mixins (see willTransitionTo), as they are considered harmful.
Although componentDidMount does not fire when changing routes for the same component, componentWillUpdate and componentWillReceiveProps do.
From there, you can detect parameter changes, and can fire your actions accordingly.
the componentWillReceiveProps,componentDidUpdate lifecycle will receive the new props and then setState to change state to trigger render method.
I just dealt with this exact issue, took me a while to figure out the problem. I'm sure everything previously suggested works great as a solution. How I solved the problem was a bit different though. My componentDidMount function was essentially just providing a state update, so I just moved the functionality directly into the state and bypassed componentDidMount.
Honestly, I'm not sure if this is best practice or not (very happy for feedback if it isn't), but it's running efficiently and works great so far.
It's the first time I use backbone-support to handle zombie views.
Before introducing it, I did not have the following problem:
this.$el.append(this.template());
// this view fills up a select with options taken from a web services
this.renderChildInto(new App.view.ResourcesView({name: "idActivityDesc", url: "/wfc-services/resources/activities"}), "#divIdActivityDesc");
// population of the forms elements according to the loaded model
this.populateSelectElements();
this.populateTextElements();
if(this.model.get('completed')) {
this.$("#active").removeAttr("disabled");
}
this.delegateEvents(this.events);
return this;
With Firefox it's all working fine. If the model is empty the select elements are going to be set up with default elements. In my case is selectedIndex to -1.
Going in debug inside the view everything seems fine. I have the problem when the view is going to be happened to the parent via the method renderChildInto. The dom is fine, but without the changes derived from the populateSelectElements() if the model is empty. If it's not empty I have no problem and the view is working fine.
I'm really puzzled about it because before the return this; statement, even in Chrome/Chromium I see the selectedIndex to -1. But in the final rendered view on the browser I see the select having selectedIndex to 0.
In the composite_view.js the called code is:
renderChild: function(view) {
view.render();
this.children.push(view);
view.parent = this;
},
renderChildInto: function(view, container) {
this.renderChild(view);
this.$(container).html(view.el);
},
Here's the end of this story.
I found from many sources, including stackoverflow, that it is possible to use selectedIndex to -1.
I'm not a frontend developer and I do not have so much experience with browser compatibility so I found the help I needed from a colleague of mine that is expert in this thing and basically suggested me that this type of usage of selectedIndex was really weird.
He suggested me to use a default option element with value to "-" or whatever was compliant with the domain to define an empty element.
Worked like a charm and since we actually did a good job in the design we had just to change the selectedIndex from -1 to 0.
I would like to re-use a Request.JSON object, but I am not sure how. I'm looking for something like the following example:
// In initialize/constructor
this.request = new Request.JSON( {
method : 'get'
});
// Elsewhere
this.request.setOptions({
url : 'http://...',
onSuccess : onSuccess,
onFailure : onFailure
}).send();
there are going to be certain issues with this kind of approach.
if you only have the one instance handling all requests, then you need to make sure whilst a request is taking place, there is nothing else that can restart it with the new options as its asynchronous. additionally, events will stack up. every new instance that you run will addEvent onComplete/onSuccess/onFailure and they won't always be relevant. so you need to apply removeEvents() to the request instance before each run.
have a look here http://www.jsfiddle.net/dimitar/8a7LG/4/
i am not showing this as an example of how i'd write it but to see the problems that come with it. click the second link first then the first one (jsfiddle adds 2 seconds network lag) and you will see the alert from the second link's onComplete event stacked up on the first one as well. further more, for each click on link 2 you will see a new alert in addition to the old ones.
you must also consider how applicable it is to extend Request.JSON instead but it all depends on your needs.
p.s. if you go over to Request.JSONP this kind of structure may play some tricks, in particular with the callback functions being reset etc.
best of luck :)
edit here's the thing working with removeEvents so you don't get the stacking up: http://www.jsfiddle.net/dimitar/8a7LG/5/
I don't differ with the accepted answer's point, but it actually sidesteps the question IMHO.
Take a look at the following blog post (it's not mine); there, under the subtitle Multiple Links with Request.HTML you can find some guidance about how to reuse the Request instance.
http://ryanflorence.com/basic-ajax-and-json-requests-with-mootools/