I can't select an element after calling cy.get('body') - testing

cy.get('body'). then(body => {
cy.wrap(body).should('have.class','.layout-header')
}
cypress doesn't find the class 'layout-header'. when I do like that then it works:
cy.get('body'). then(body => {
cy.get('.layout-header')
}
I need this because I want to use conditional testing like this:
cy.get('body').then(($body) => {
// synchronously ask for the body's text
// and do something based on whether it includes
// another string
if ($body.text().includes('some string')) {
// yup found it
cy.get(...).should(...)
} else {
// nope not here
cy.get(...).should(...)
}
})
could you tell me why?, thanks

The first part of your test cy.wrap(body).should('have.class', '.layout-header') is looking for a class called .layout-header to exist on the body element, not a child element within the body element.
I'm sure you've already read how conditional testing in Cypress is not advised, but with that in mind, this should put you on the right path to conditionally check for an elements' existence:
cy.get("body").then(body=>{
// This will perform a document query on your body element and
// give you a static node list of child elements have the class '.layout-header'
const hasClass = body[0].querySelectorAll(".layout-header")
if(hasClass.length > 0)
{
//Code to execute if the class exists in the body
}else{
//Code to execute if the class DOES NOT exist in the body
}
})
Working example against a demo test site:
describe("working example", ()=>{
it("check body for elements with class name 'custom-control-label'", ()=>{
cy.visit("https://demoqa.com/automation-practice-form")
cy.get("body").then(body=>{
const hasClass = body[0].querySelectorAll(".custom-control-label")
if(hasClass.length > 0)
{
cy.log(`The class 'custom-control-label' was found in the body ${hasClass.length} times.`)
}else{
cy.log("The class 'custom-control-label' was NOT found in the body.")
}
})
})
})

Related

Mocking a return value for a Subject - Unit Testing with Jasmine

I'm unit testing and part of the testing has a Subject. I'm new to Subjects and get the general gist of how they work but am struggling to mock a return value on one. I've tried various ways in the hopes of stumbling on the correct way like using a spy and returnvalue to return the number 3.
In the component:
....
private searchEvent: Subject<string> = new Subject<string>();
....
this.searchEvent.pipe(debounceTime(500)).subscribe(value => {
if (value.length >= 3) {
this.retrieveAssets(value);
}
})
....
In my spec file I basically have:
component['searchStockEvent'].subscribe(x=> of(3));
fixture.whenStable().then(() => {
expect(component['retrieveAssets']).toHaveBeenCalled();
});
searchEvent being private will make it difficult to call next directly on the subject so you have to find a way of what makes searchEvent emit a value of greater than 3 and go that route.
For this demo, we will make it public:
....
public searchEvent: Subject<string> = new Subject<string>();
....
this.searchEvent.pipe(debounceTime(500)).subscribe(value => {
if (value.length >= 3) {
this.retrieveAssets(value);
}
})
....
import { fakeAsync, tick } from '#angular/core/testing';
it('should call retrieve assets', fakeAsync(() => {
component.searchEvent.next(3);
// we need to pass 501ms in a fake way
tick(501);
expect(component.retreiveAssets).toHaveBeenCalled();
}));

simply replace a node's content in prosemirror

I'm in a function that receives a string as input:
(text) => {
}
I have access to the editor via Vue props (props.editor). I would like to replace the current node's content with this text. I cannot seem to find out how to do this. I'm using tiptap2, which is a wrapper around ProseMirror and has access to all of ProseMirror's api.
I'd rather not try to replace the whole node unless necessary, which I also tried, doing below – but cannot get that to work either:
(text) => {
props.editor
.chain()
.focus()
.command(({ tr }) => {
const node = props.editor.state.schema.nodes.paragraph.create(
{ content: text}
);
tr.replaceSelectionWith(node);
return true;
})
.run();
}
Much thanks
This solution works for me in Tiptap version 2.
A precondition for this to work is, that the text to be replaced is marked (highlighted).
const selection = editor.view.state.selection;
editor.chain().focus().insertContentAt({
from: selection.from,
to: selection.to
}, "replacement text").run();
I'm late to the party but this is the top result I came across when trying to find a solution for myself.
My code is in the context of a React NodeView, so I'm given a getPos() prop that gives the position of the React node in the Prosemirror document (I believe this number more-or-less means how many characters precede the React NodeView node). With that I was able to use this command chain to replace the content:
import { Node as ProsemirrorNode } from "prosemirror-model";
import { JSONContent, NodeViewProps } from "#tiptap/react";
const NodeViewComponent = (props: NodeViewProps) =>
// ...
/**
* Replace the current node with one containing newContent.
*/
const setContent = (newContent: JSONContent[]) => {
const thisPos = props.getPos();
props.editor
.chain()
.setNodeSelection(thisPos)
.command(({ tr }) => {
const newNode = ProsemirrorNode.fromJSON(props.editor.schema, {
type: props.node.type.name,
attrs: { ...props.attrs },
content: newContent,
});
tr.replaceSelectionWith(newNode);
return true;
})
.run();
};
// ...
};
Basically you want to:
Set the current selection to the node you want to replace the content of
Create and update a new node that is a copy of the current node
Replace your selection with the new node.

TestCafe: Selector within Selector

I work on a set of helpers for my page model.
This is how the DOM may look like:
<div id="parentA">
<div class="child yes">hello</div>
<div class="child">world</div>
</div>
<div id="parentB">
<div class="child no">hello</div>
<div class="child">world</div>
</div>
Now I want to inspect one of the.child elements within #parentA or #parentB.
import { Selector } from "testcafe";
fixture `children`
.page `http://localhost:8080/index.html`;
// an example of what I expect.
// this is not how i want to write tests.
test("hard-coded: child in A has class 'yes'", async (t) => {
const yesChild = Selector("#parentA .child").withText("hello");
t.expect((await yesChild.classNames).includes("yes"));
});
// helper function for the page model (in a shared module later)
function getParent(name: string) {
return Selector(`#parent${name}`);
}
// helper function for the page model (in a shared module later)
function getChild() {
return Selector(".child");
}
// this is how I want to write tests.
test("parametric-find: child in A has class 'yes'", async (t) => {
const parent = getParent("A");
const child = getChild().withText("hello");
const yesChild = parent.find(child); // there is no overload for find that takes another Selector.
t.expect((await yesChild.classNames).includes("yes"));
});
I think one solution could be a function like this:
async function withinParent(child: Selector, parent: Selector): Selector {
// how should I implement this?
}
Another solution could be a higher order function that creates the filterFunction:
test("parametric-find-descendantChild: child in A has class 'yes'", async (t) => {
const parent = getParent("A");
const child = getChild().withText("hello");
const yesChild = parent.find(descendantChild(child));
t.expect((await yesChild.classNames).includes("yes"));
});
function descendantChild(child: Selector): (node: Element) => boolean {
// how should I implement this?
}
but all the approaches I can think of lead to dead-ends.
parent and child could match multiple elements
there is no easy way to access an element selected by a selector in order to compare it to another selector
how do I compare Selectors?
functions/callbacks that operate on the Element level are executed in the browser. How cold I pass a Selector or the Elements matched by a selector into a browser function?
Well, should I write a feature request, or is there a smart way to do this?
You can chain Selector methods to achieve this.
function getParent(name) {
return Selector(`#parent${name}`);
}
function getChildren(selector) {
return selector.child('.child');
}
test(`parametric-find: child in A has class 'yes'`, async (t) => {
const parent = getParent('A');
const child = getChildren(parent).withText('hello');
await t.expect(child.classNames).contains('yes');
});

VueJS: Adding a class to one element if a class exists on another element

I'm working in VueJS. I'm trying to bind a class to one element based on an existence of a class on another element. The below is in a :for loop to print out the list.
The '#accordion-'+(index+1)) is the id of the div I want to check to see if a class exists on it.
I wrote a method and it works UNTIL I check the element's classList. Right now, I'm only doing a console log, but eventually this will return true and hopefully the class will apply.
methods: {
existingTopic: function(lcDivID) {
const element = document.querySelector(lcDivID);
console.log(element); //It gives me the element.
/* The below is where it crashes */
console.log(element.classList.contains("collapsePanelExistingTopic"));
}
}
I find it so frustrating. I've spent a day on this without any results. Any help you can provide it would be great.
Here it is, you can also use this.$el as document
...
methods: {
hasClass() {
const element = this.$el.querySelector('h1')
if (element.classList.contains('your-class-here')) {
console.log(true)
this.$el.querySelector('anotherelement').classList.add("your-another-class");
} else {
console.log(false)
}
}
},
mounted() {
this.hasClass()
}
...
Alternative
<h1 class="mb-5 fs-lg" ref="myElement">Sign up</h1>
...
methods: {
hasClass() {
const element = this.$refs.myElement
if (element.classList.contains('mb-5')) {
console.log(true)
this.$el.querySelector('anotherelement').classList.add("your-another-class");
} else {
console.log(false)
}
}
},
mounted() {
this.hasClass()
}
...
So you can define ref as :ref="index+1" in your loop

Aurelia `click` attribute that requires event target to be same as element

I'm aware of click.trigger as well as click.delegate which work fine. But what if I want to assign a click event that should only trigger when the exact element that has the attribute gets clicked?
I'd probably do something like this were it "normal" JS:
el.addEventListener('click', function (e) {
if (e.target === el) {
// continue...
}
else {
// el wasn't clicked directly
}
});
Is there already such an attribute, or do I need to create one myself? And if so, I'd like it to be similar to the others, something like click.target="someMethod()". How can I accomplish this?
Edit: I've tried this which doesn't work because the callback function's this points to the custom attribute class - not the element using the attribute's class;
import { inject } from 'aurelia-framework';
#inject(Element)
export class ClickTargetCustomAttribute {
constructor (element) {
this.element = element;
this.handleClick = e => {
console.log('Handling click...');
if (e.target === this.element && typeof this.value === 'function') {
console.log('Target and el are same and value is function :D');
this.value(e);
}
else {
console.log('Target and el are NOT same :/');
}
};
}
attached () {
this.element.addEventListener('click', this.handleClick);
}
detached () {
this.element.removeEventListener('click', this.handleClick);
}
}
And I'm using it like this:
<div click-target.bind="toggleOpen">
....other stuff...
</div>
(Inside this template's viewModel the toggleOpen() method's this is ClickTargetCustomAttribute when invoked from the custom attribute...)
I'd also prefer if click-target.bind="functionName" could instead be click.target="functionName()" just like the native ones are.
Just use smth like click.delegate="toggleOpen($event)".
$event is triggered event, so you can handle it in toggleOpen
toggleOpen(event) {
// check event.target here
}
Also you can pass any other value available in template context to toggleOpen.