invoke() method on cypress doesn't work if called twice - testing

I'm new with Cypress and I'm trying to implement some easy tests using an already existing webpage. I'm a little bit confused about the result, because I call invoke() twice: the first time to check the initial value (0%), and the second one to set a new value and check the change, but it doesn't work, and it tells me that it cannot find the attribute I'm searching for. The code is the following:
describe('My first test', function(){
beforeEach(() => {
cy.visit("https://www.wikiwand.com/en/IPv4")
})
it('test1', function() {
const opt = cy.get("#main_menu > li").eq(3).click()
const sty = opt.get(".noUi-origin").first()
sty.invoke("attr", "style").should("include", "left: 0%;")
sty.invoke("attr", "style", "left: 100%;").should("have.attr", "style", "left: 100%;")
})
})
I simply take the personalize button on the menu bar, and I want to change the value serif or sans. There is a problem with the order of the two invoke()? The error is:
*Timed out retrying after 4000ms: cy.invoke() errored because the property: attr does not exist on your subject.
cy.invoke() waited for the specified property attr to exist, but it never did.
If you do not expect the property attr to exist, then add an assertion such as:
cy.wrap({ foo: 'bar' }).its('quux').should('not.exist')*
on
sty.invoke("attr", "style", "left: 100%;").should("have.attr", "style", "left: 100%;")
Has someone an idea about it?

So the Font slider when it is Serif the style is left: 0%; and when you drag the slider to Sans the style is left: 100%;. So your test should look like this:
cy.visit("https://www.wikiwand.com/en/IPv4");
cy.get("#main_menu > li").eq(3).click();
cy.get(".noUi-origin")
.first()
.invoke("attr", "style")
.should("include", "left: 0%;");
cy.get('[ng-click="$root.fontStyleHandler(1, true)"]').click(); //Drags the slider from Serif to Sans
cy.get(".noUi-origin")
.first()
.invoke("attr", "style")
.should("include", "left: 100%;");
Or, if you don't want to use the slider then you have to first remove the style attribute and then add the style attribute with value left: 100%;, in that case your test should look like:
cy.visit("https://www.wikiwand.com/en/IPv4")
cy.get("#main_menu > li").eq(3).click()
cy.get(".noUi-origin")
.first()
.invoke("attr", "style")
.should("include", "left: 0%;")
cy.get(".noUi-origin").first().invoke("removeAttr", "style")
cy.get(".noUi-origin").first().invoke("attr", "style", "left: 100%;")
cy.get(".noUi-origin")
.first()
.invoke("attr", "style")
.should("include", "left: 100%;")

Cypress commands run in a "chain", with the current "subject" being passed from one command to the next.
Although you think you are saving a reference to the element in const sty = ..., actually you are saving a pointer to the internal Cypress subject.
When you do sty.invoke("attr", "style"), you have now changed the subject to that style attribute, not the element.
So when you try to sty.invoke("attr", "style") again, sty no longer has an attr method, hence the error.
More conventional way is not to store command results.
Just re-query
const opt = cy.get("#main_menu > li").eq(3).click()
cy.get(".noUi-origin").first()
.invoke("attr", "style")
.should("include", "left: 0%;")
cy.get(".noUi-origin").first()
.invoke("attr", "style", "left: 100%;")
.should("have.attr", "style", "left: 100%;")
Or use an assertion that does not change the subject
const opt = cy.get("#main_menu > li").eq(3).click()
cy.get(".noUi-origin").first()
.should("have.css", "left", "0px") // keeps the same subject
.invoke("attr", "style", "left: 100%;")
.should("have.attr", "style", "left: 100%;")

Related

Identifying a Customized Built-In Element without an is= attribute

In my Chessly.github.io project I use Customized Built-In IMG Elements to define SVG chesspieces:
Question: How can I distinguish a regular IMG from a customized IMG?
document.body.append(
document.createElement("IMG", {
is: "white-queen"
});
);
This creates a chesspiece, but does not set the is= attribute
I now explicitly set the is= attribute myself, but since this attribute does nothing and can be set to any value (I use is as an observed attribute in my Custom Element code) it is not a solid way to distinguish IMG elements from Customized IMG elements when walking the DOM.
If I promote a pawn (replace the img src)
<img is=white-pawn/>
with element.setAttribute("is","white-queen")
How can I determine the piece originally was the white pawn?
It still is a white-pawn in the Custom Elements registry.
Am I overlooking something?
Simplified code (with basic SVG shape) in JSFiddle: https://jsfiddle.net/dannye/k0va2j76/
Update: Code (based on correct answer below)
let isExtendedElement = x =>
Object.getPrototypeOf(x).__proto__.constructor.name !== 'HTMLElement';
note! This does NOT catch Autonomous Custom Elements!
maybe better:
let isBaseElement = x =>
Object.getPrototypeOf(Object.getPrototypeOf(x)).__proto__.constructor.name=='Element';
I think adding explicitly the is attribute is the best solution for now.
Else you'll have to deal with the class reference. In your case:
yourElement.constructor === customElements.get( 'circle-image' )
yourElement.constructor === CircleImage //if it's the named class
This supposes that you know the name of the custom elements you want to check.
Else you'll have to go through the protoype chain:
CircleImage --> HTMLImageElement --> HTMLElement --> Element --> Node
If HTMLElement is only the father, it's a built-in element.
If HTMLElement is the grandfather (or grand-grand...), it's probably an extended build-in element.
update
If you use a named class you can also retrieve its name:
elem.constructor.name

Datatable dom l filter has no label

I'm using a theme, and somehow giving the dom option of l only shows the dropdown without the label.
<Show [dropdown here] entries>
How do I get the label to show, and how to customise the labels?
Example here with the label:
https://plnkr.co/edit/eJTN86XnfeDKSBUfaHlJ?p=preview
Found the culprit
The sLengthMenu is overriding the default text for the filter list.
Just had to remove it.
oLanguage: {
"sLengthMenu": "_MENU_"
}

how to obtain a resizable dijit dialog

In dojo I intend to use my dialog box resizable by dragging the mouse over the rightmost corner edge. Dialog as such has no property to resize it. So I try to use Floating pane and then add the dialog as child. I plan to use the resizable property of Floating Pane for the child i.e dialog. Is this approach wrong ?
d = new Dialog({
title: "Testing Dialog",
content: "hi"
});
fp = new FloatingPane({
title: "Test",
resizable: true,
dockable: false,
style: "position:absolute;top:0;left:0;width:100px;height:100px;visibility:hidden;",
id: "fp"
}, dojo.byId("fp"));
fp.addChild(d);
fp.startup();
Thanks for the question and the advice Richard. This worked for me, I add it it here if someone else need something similar:
define([
'dojo/_base/declare',
'dijit/_TemplatedMixin',
'dijit/_WidgetsInTemplateMixin',
'dojo/text!./DialogResize.html',
'dojox/layout/ResizeHandle',
'dijit/Dialog'], function (
declare,
_TemplatedMixin,
_WidgetsInTemplateMixin,
template,
ResizeHandle,
Dialog
) {
return declare('app.Dialog.Resize', [Dialog, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: template,
// resizeAxis: String
// One of: x | xy | y to limit pane's sizing direction
resizeAxis: "xy",
postMixInProperties: function() {
this.inherited(arguments);
//console.log('DialogResize');
},
startup: function() {
//console.log('DialogResize startup');
// The orginal template was modifed by adding a resizeHandle handle, which is then initialised here
this._resizeHandle = new ResizeHandle({
targetId: this.id,
resizeAxis: this.resizeAxis
}, this.resizeHandle);
}
});
});
Then DialogResize.html:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"
role="heading" level="1"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabindex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent"></div>
${!actionBarTemplate}
<span dojoAttachPoint="resizeHandle" class="dojoxFloatingResizeHandle"></span>
</div>
It really depends on what aspect of the Dialog you're trying to replicate from the FloatingPane. If, for instance you want the default action pane and/or the Dialog overlay, then perhaps it's a better idea to just extend the Dialog and add a resize handler like the Floating Pane has.
If there are more aspects of a Floating Pane you like (e.g., moveable, resizeable, locked into a parent window), then perhaps you should extend the FloatingPane and add the properties of a Dialog that you like.
Either way though, it really depends on what you're trying to accomplish with this new Dialog and what features you need. Extending either would be my suggestion. I would not, however recommend accomplishing either by placing a Dialog into a Floating Pane because I can't see how it would accomplishes what you're trying to do. If you could explain your use case more, then I can give you a more concrete example.
[Edit]
You'll probably want to look at this: http://dojotoolkit.org/reference-guide/1.10/quickstart/writingWidgets.html to learn more about how to extend widgets.
Looking at the Floating Pane code, what you'll need to do is to add a resizeHandle to your extended Dialog that looks like this:
<span dojoAttachPoint="resizeHandle" class="dojoxFloatingResizeHandle"></span>
You'll then need to initialize it by doing something like this:
this._resizeHandle = new ResizeHandle({
targetId: this.id,
resizeAxis: this.resizeAxis
},this.resizeHandle);
Resize handle can be found at dojox/layout/ResizeHandle

Always show dojo tooltip

I have the following code for adding tooltip for onclick of a span id as shown below:
new dijit.Tooltip({
connectId: ['gridEditHelp'],
label: 'Double click on an item in the below table to perform inline editing of attributes',
position: ['above']
});
The problem is that I want the tooltip to be visible always on the web page.
Any ideas or existing API available for the same?
Use TooltipDialog instead - or else you will have to mess with the _MasterTooltip manager (there's more or less only one global, reusable tooltip). Then call dijit.popup.open - and never call .close
dijit.popup.open({
popup: new TooltipDialog({
id: 'myTooltipDialog',
style: "width: 300px;",
content: "<p>I have a mouse leave event handler that will close the dialog."
});,
around: dojo.byId('thenode')
});

Dojo when drop[dnd drag drop] it creates a new div

As title, how to make this? I have already done drag and drop using dnd.Source and dnd.Target. Have no idea if it possible to make it when drop and it creates/generates a div which it can closable.
I think it would be possible to hook up to the onDndDrop event of your dnd.Target by doing some along these lines:
dojo.connect(yourtarget, "onDndDrop", _dndHandler);
Then in the _dndHandler function you could create a new div using the dojo.create and dojo.place:
var newdiv = dojo.create("div", { innerHTML: "This is my new div" });
dojo.place(newdiv, dojo.byId("your_target_id", "first"));
Hope it helps
/Daniel