CKEditor 5 Mention plugin - append child to attribute element - ckeditor

https://ckeditor.com/docs/ckeditor5/latest/features/mentions.html
I'd like to insert <img inside 'a' element in downcast. It can be 'a', 'span' whatever. The problem is that this is AttributeElement and I can't create img and insert it like
writer.insert(writer.createPositionAt(myAttributeElement, 0), myImage) because I get an error view-writer-invalid-position-container. I know that it would work if downcast was elementToElement with 'createContainerElement' instead of 'createAttributeElement' but the Mention plugin expects to have downcasting written as it is. Can I in someway append actually any other element inside AttributeElement?

Related

cypress - deleting a sub-row while trapped inside .within and .then

I have a EditParentAndChildren screen where I want a test that:
navigates to page
remembers the name of the parent
pick one of the children rows
remember its id/name
delete it via the Trashcan button on that row
save
navigate to a View
ensure the parent's name appears and the deleted child's name does not
I can't seem to pluck text off of the screen and put it into one of Cypress's #alias variables, and standard js variables aren't allowed by cypress. So, I use .then to get the value that way.
But when I choose a child row and go .within to get its name and click its delete button, I can't then issue the final assertions for the test because I'm still in the .within, I can't escape the .within because the .then for getting the child's name is completely inside, and, trying to .root().closest() doesn't work because the <tr> I'm in is not only getting deleted but I'm doing a page nav afterward.
cy.get('[name=parentname]')
.invoke('val')
.then(parentName => {
cy.get('[class^=childrenTable]')
.find('[name=child_id]')
.first()
.parents('tr')
.within(tr => {
cy.get('[name=child_id]')
.invoke('val')
.then(nameOfchildToDelete => {
// delete this child
cy.get('[class*=trash]').click();
cy.get(loadingSpinner).should('not.exist');
// ERROR can't find submit button, you are still .within the <tr>
cy.contains(/Submit/i).click();
cy.url().should('match', /parent\/\d+$/);
cy.get(loadingSpinner).should('not.exist');
cy.contains('[class*=breadcrumb_currentcrumb]', parentName).should('exist');
cy.contains('table', nameOfChildToDelete).should('not.exist');
});
});
});
One solution is simply never to use .within. Formerly I was selecting the row, then within it selecting & using each piece of the row. Instead, select each piece of a row using the same selector that selects that row.
Not this:
cy.get('[class^=childrenTable]')
.find('[name=child_id]')
.first()
.parents('tr')
.within(tr => {
cy.get('[name=child_id]')
.invoke('val')
.then(nameOfchildToDelete => {
More like this:
cy.get('[class^=childrenTable] [name=sample_id]:first-child')
.invoke('val')
.then(nameOfSampleToDelete => {
// etc...
cy.get('[class^=childrenTable] [class*=trash]').first().click();
Code inside a .then is just like outside the .then excepting the level of indent, so most of the code is the same. But code inside a .within is kind of at a dead-end. You can't return values from a .within and can't set state or js vars from the outer context.
So: don't use .within, always use long selectors, and don't worry about picking "sections" like a particular <tr> or a particular card in a FlexBox for re-use.
If the selectors are very long consider moving them to a const string outside of the file and possibly concatting them if need be. But generally in Cypress trying to enter into a context is something of an anti-pattern.

Testing text of an element using Cypress

I'd like to validate the text of an element (p element, for instance) with the help of Cypress.
I have used this code:
cy.get('#word').should('have.value', 'Color')
and I received this:
expected <p#word> to have value Color, but the value was ''
Evidently, it validates the CSS but not the html element value. How can I validate the element content here?
If you are asserting the inner Text, instead of have.value you have to use have.text.
cy.get('#word').should('have.text', 'Color')
Or, If you want to assert a partial string, you can use include.text
cy.get('#word').should('include.text', 'Color')

Cypress - counting number of elements in an array that contain a specific string

Attempting to confirm that of all the schema in the head of a page exactly 3 of them should have a specific string within them. These schemas have no tags or sub classes to differentiate themselves from each other, only the text within them. I can confirm that the text exists within any of the schema:
cy.get('head > script[type="application/ld+json"]').should('contain', '"#type":"Product"')
But what I need is to confirm that that string exists 3 times, something like this:
cy.get('head > script[type="application/ld+json"]').contains('"#type":"Product"').should('have.length', 3)
And I can't seem to find a way to get this to work since .filter, .find, .contains, etc don't filter down the way I need them to. Any suggestions? At this point it seems like I either need to import a custom library or get someone to add ids to these specific schema. Thanks!
The first thing to note is that .contains() always yields a single result, even when many element match.
It's not very explicit in the docs, but this is what it says
Yields
.contains() yields the new DOM element it found.
If you run
cy.get('head > script[type="application/ld+json"]')
.contains('"#type":"Product"')
.then(console.log) // logs an object with length: 1
and open up the object logged in devtools you'll see length: 1, but if you remove the .contains('"#type":"Product"') the log will show a higher length.
You can avoid this by using the jQuery :contains() selector
cy.get('script[type="application/ld+json"]:contains("#type\": \"Product")')
.then(console.log) // logs an object with length: 3
.should('have.length', 3);
Note the inner parts of the search string have escape chars (\) for quote marks that are part of the search string.
If you want to avoid escape chars, use a bit of javascript inside a .then() to filter
cy.get('script[type="application/ld+json"]')
.then($els => $els.filter((index, el) => el.innerText.includes('"#type": "Product"')) )
.then(console.log) // logs an object with length: 3
.should('have.length', 3);

How to iterate on select elements with Xpath with one exception?

I want to iterate over each selector found that contains a specific class in order to retrieve all elements within the divs. This works until it reaches one item containing an ID.
for selector in response.xpath("//div[#class='product-list-entry']"):
My best try to get around this is the following code:
for selector in response.xpath("//div[not(#id) and #class='product-list-entry']"):
Both versions lead to only retrieving 5 result sets instead of the full list.
How can I simply ignore the one with the id and iterate on all others?
This should extract the content of the specific divs (examples : text of the div, content of a span and text of a p element) :
def parse(self, response):
for selector in response.xpath("//div[#id='product-list']"):
content = selector.xpath(".//div[not(#id)]/text()").extract()
content2= selector.xpath(".//div[not(#id)]/span").extract()
content3= selector.xpath(".//div[not(#id)]/p/text()").extract()
content4= ...
print (content,content2,content3,...)

XSLTForms using position() to select repeat element from another instance

Background:
Building a form (using XSLTForms) that is submitted, then the user may resubmit at a later date. We want the user to see the values of their previous submission next to the form input, so we're loading 2 instances 'data-set' and 'old-data-set'. This is working in most instances, but not where we have repeats. for whatever reason the position() method always returns '1' when calling data from the 'old-data-set' instance. For example:
<xf:repeat nodeset="instance('data-set')/references/reference">
<xf:group ref=".">
<xf:label>Reference <xf:output value="position()"/></xf:label>
<xf:input ref="/org_name">...</xf:input>
<xf:output ref="instance('old-data-set')/references/reference[position()]/org_name"/>
</xf:group>
</xf:repeat>
The position() method works in the label, but always returns '1' when attempting to get the value from the second instance. For example the above produces:
<label>Reference 1</label>
<input>Org name 1</input>
<output>Old org name 1</output>
<label>Reference 2</label>
<input>Org name 2</input>
<output>Old org name 1</output>
<label>Reference 3</label>
<input>Org name 3</input>
<output>Old org name 1</output>
How can I call the position() of the repeat so I can use it to get the correct value from the 'old-data-set' instance?
The reason this is happening is that inside the predicate (inside the [], the context is different and position() refers to the position of the old-data-set reference node rather than the current reference node in your iteration.
I'm not familiar with XSLTForms, but how's this?
<xf:repeat nodeset="instance('data-set')/references/reference">
<xf:group ref=".">
<xf:label>Reference <xf:output value="position()"/></xf:label>
<xf:input ref="/org_name">...</xf:input>
<xf:output
ref="instance('old-data-set')/references/reference[count(current()/preceding-sibling::reference) + 1]/org_name"/>
</xf:group>
</xf:repeat>

Resources