I have the following Xpath to locate an element which has the text Manager. I need to use CSS in our Cypress IO automation. My test adds a new item in the Gui by clicking the Add button and types in Manager and clicks the Save button. I want to do an Assertion the text Manager exists when it has been saved. the Assertion is not working due to not finding the correct locator.
Xpath locator:
//span[contains(text(),'Manager')]
I tried to use the following as CSS
cy.get('span:contains("Operator")
The locator is not working I get the following error when i run my code
o
find.form-control3
o 14assertexpected [ <span.form-control>, 2 more... ] to have text Manager, but the text was ManagerUndefinedManager3
AssertionError
Timed out retrying after 10000ms: expected '[ <span.form-control>, 2 more... ]' to have text 'Manager', but the text was 'UndefinedManager'
The first item by default in the list of elements is called Undefined. When a new entry is added it is Manager in the list.
My code snippet is:
static get AudienceTextfieldSavedValue(): Cypress.Chainable<JQuery<HTMLElement>> {
//return cy.get('span:contains("Manager")').should('have.text', 'Manager');
//return cy.get('(ul).find(li with given text)..find(form-control).have "Manager")')
return cy.get('.cdk-drop-list').find('.form-control').should('have.text', 'Manager');
}
it('Add Audience', () => {
Lists.navigateToLists;
Audience.clickAdd;
Audience.AudienceTextfield.type("Manager");
Audience.getSaveButton.click().then(() => {
cy.wait(['#savingAudienceRecord']);
});
Audience.AudienceTextfieldSavedValue.should('have.text', 'Manager');
});
The HTML snippet is:
What is the correct css locator I should use please? Thanks
Riaz
In my understanding, the simplest reproduction of what happens during the test is
DOM starts with two spans
<ul class="cdk-drop-list">
<li><span class="form-control">Manager</span></li>
<li><span class="form-control">Undefined</span></li>
</ul>
You add the new entry and the DOM becomes
<ul class="cdk-drop-list">
<li><span class="form-control">Manager</span></li>
<li><span class="form-control">Undefined</span></li>
<li><span class="form-control">Manager</span></li>
</ul>
You want to check the last entry, so AudienceTextfieldSavedValue() should only select the last span in the list (assuming the save action does not sort the list).
static get AudienceTextfieldSavedValue(): Cypress.Chainable<JQuery<HTMLElement>> {
return cy.get('.cdk-drop-list').find('.form-control')
.last() // assuming the saved field is the last one
.should('have.text', 'Manager');
}
it('Add Audience', () => {
Lists.navigateToLists;
Audience.clickAdd;
Audience.AudienceTextfield.type("Manager");
Audience.getSaveButton.click().then(() => {
cy.wait(['#savingAudienceRecord']);
});
Audience.AudienceTextfieldSavedValue.should('have.text', 'Manager');
});
.should('have.text', 'Manager') is performed twice, so you can perhaps remove it from AudienceTextfieldSavedValue() and just return the last entry.
That way, you can test with different text entry.
You may also want check the number of entries increases from 2 to 3, because if Audience.getSaveButton.click() fails to do the save your test would still pass.
static get AudienceTextfieldSavedValue(): Cypress.Chainable<JQuery<HTMLElement>> {
return cy.get('.cdk-drop-list').find('.form-control')
.last() // assuming the saved field is the last one
}
static get AudienceTextfieldCount(): Cypress.Chainable<JQuery<HTMLElement>> {
return cy.get('.cdk-drop-list').find('.form-control')
.its('length')
}
it('Add Audience', () => {
Lists.navigateToLists;
Audience.AudienceTextfieldCount().should('eq', 2)
Audience.clickAdd;
Audience.AudienceTextfield.type("Manager");
Audience.getSaveButton.click().then(() => {
cy.wait(['#savingAudienceRecord']);
});
Audience.AudienceTextfieldCount().should('eq', 3)
Audience.AudienceTextfieldSavedValue.should('have.text', 'Manager');
});
Related
I´m trying to create a "one line" message component based on Lexical, but i´m unable to prevent the enter key to create a new paragraph.
Any ideas how to accomplish this?
I´ve added styling with
white-space: nowrap!important;
resize: none;
and i´ve tried to MaxLengthPlugin ( which works but if enter in there it creates two lines)
also tried to add
<EditorWrapper ref={onRef} data-testid="editor"
onKeyDown={event => {
if (event.key === 'Enter') {
event.preventDefault();
}
}}>
I was expecting this to prevent the new paragraph with enter, but still adds a new paragraph in the editor.
I was able to create a single line editor by using registerNodeTransform to remove the LineBreakNode as soon as it's created. It doesn't feel like the best solution, but it works.
editor.registerNodeTransform(LineBreakNode, (node) => {
node.remove();
});
I also explored listening to the KEY_ENTER_COMMAND command, but that didn't prevent newlines from being pasted into the editor.
You can register the command INSERT_PARAGRAPH_COMMAND and then return true. By returning true you tell lexical that you have handled the paragraph insert and the default lexical code will not execute.
Example:
useEffect(() => {
return editor.registerCommand(
INSERT_PARAGRAPH_COMMAND,
() => {
return true;
},
COMMAND_PRIORITY_EDITOR
);}, [editor]);
I have MS Word documents where the table of contents, built using title 1 to title 4, is a hierarchy of more than 100 items.
I want to use Office JS to develop an add-in to import these documents in WordPress as a set of Pages with the same hierarchy as one of the tables of contents.
Each WP page would contain the HTML of all the paragraphs contained under each title level.
Looking at the Office JS samples, I have been able to log to the console the outline levels of all paragraphs in the document, but I am stuck with getting the HTML.
I think this is probably because I misunderstand context.sync().
Here is my code:
$("#exportToCMS").click(() => tryCatch(exportToCMS));
function exportToCMS() {
return Word.run(function (context) {
// Create a proxy object for the paragraphs collection
var paragraphs = context.document.body.paragraphs;
// Queue a command to load the outline level property for all of the paragraphs
paragraphs.load("outlineLevel");
return context.sync().then(function () {
// Queue a a set of commands to get the HTML of each paragraph.
paragraphs.items.forEach((paragraph) => {
// Queue a command to get the HTML of the paragraph.
var ooxml = paragraph.getOoxml();
return context.sync().then(function () {
console.log(ooxml.value);
console.log(paragraph.outlineLevel);
});
});
});
});
}
/** Default helper for invoking an action and handling errors. */
function tryCatch(callback) {
Promise.resolve()
.then(callback)
.catch(function (error) {
console.error(error);
});
}
If I comment out the line which logs ooxml.value, the script runs fine.
When uncommented, I get an error "Unhandled promise rejection".
Broken promise chains are common when you have a context.sync inside a loop. This is also bad from a performance standpoint. Your first step to fixing your code is to get the context.sync out of the forEach loop by following the guidance in this article: Avoid using the context.sync method in loops.
I'm currently developing a simple chat (like Facebook's) for a web application.
I'm using Angular 6 as framework and Firebase as my database.
When a user clicks on a certain conversation, I'm retrieving the previous messages using the .on("child_added, //function here) and then I'm pushing the results into an array which will be displayed using an *ngFor loop.
The weird issue I'm encountering is that the messages will be displayed sometimes correctly, and sometimes not displayed at all. When this happens, as soon as I click anywhere, the *ngFor will be called again and everything looks fine.
Using console.logs I found out that the data is pushed correctly into the array every time, but, for some reason, the ngFor (same with ngAfterViewChecked) sometimes will update and sometimes will just return an empty array.
My .ts file (relevant parts):
ngAfterViewChecked() {
/*
when everything works, this will be called everytime something changes
(even after the array gets populated)
when the issue comes up, this will be called as usual but it won't be called after the array gets populated,
thus returning an empty array the last time it was called
*/
console.log(this.msgService.messageList)
}
//this will be called everytime a user clicks on a conversation on the left sidebar
getSpecConversation(conversation) {
//emptying the array
this.msgService.messageList = [];
//calling the .off using previous conversation key
this.msgService.getConversation(this.msgService.selectedConversation.$key)
.off('child_added');
//getting messages of currently selected conversation
this.msgService.getConversation(conversation.$key)
.on('child_added', (data => {
//push the data into array
this.msgService.messageList.push(data.val());
})
);
//storing currently selected conversation
this.msgService.selectedConversation = conversation;
}
the Template (relevant parts):
<ul class="conversation-list">
<li *ngFor="let message of msgService.messageList" class="conversation-item" [class.sender]="message.byUid == userInfoService.userUid">
<p class="message">{{message.msg}}</p>
<p class="msg-time">
<span>{{message.time | date: 'shortTime'}}</span></p>
</li>
</ul>
The Service:
getConversation(sid: string) {
return this.database.ref('/messages')
}
Thanks for the help
Update
If it helps, if I delete the .off('child_added') method, the problem won't show up anymore, but, when I send a new message, I would have the new message called multiple times in the view (not in database).
Update your template and check that the array has data before you show it.
<ul class="conversation-list" *ngIf="msgService.messageList && msgService.messageList.length > 0">
<li *ngFor="let message of msgService.messageList" class="conversation-item" [class.sender]="message.byUid == userInfoService.userUid">
<p class="message">{{message.msg}}</p>
<p class="msg-time">
<span>{{message.time | date: 'shortTime'}}</span></p>
</li>
</ul>
It could be a race condition - the ngFor loop could run before the array has any data in it, and then when you click around the page, a hook is running telling the ngFor to run again - this time finding data.
When you run the ngIf before it will check for data, as soon as it's available it allows the ngFor to run which will now have data to iterate over and display in your list.
You're getting the data from a service, so it could be that you need to use the async pipe.
<li *ngFor="let message of msgService.messageList | async">
<p class="message">{{message.msg}}</p>
</li>
(Super new to meteor)
I've gone through the meteor todo app tutorial and on the checking off and deleting tasks part It says that
If you try checking off some tasks after adding all of the above code, you will see that checked off tasks have a line through them. This is enabled by the following snippet:
<li class="{{#if checked}}checked{{/if}}">
With this code, if the checked property of a task is true, the checked
class is added to our list item. Using this class, we can make
checked-off tasks look different in our CSS.
Is there any way to have this apply to a different tag, and not the list? so basically have it say
With this code, if the checked property of a task is true, the updatedstyle
class is added to our img tag. Using this class, we can make
checked-off tasks make images look different in our CSS.
You can put the {{#if}} anywhere you want in your template, not just in the list item. If I understand your specific question:
<li>Some item
<img class="{{#if checked}}updatedstyle{{/if}}" src="/myImg.png">
</li>
Per #blueren, the parameter to the {{#if}} is evaluated as a truthy or falsy value. It can be a key in the current data context (i.e. this.checked) or can be returned by a helper.
If you want a class reactively binded to html tag in the template, you can do it like that:
Template code:
<template name="MyTemplate">
<img src="my/img/path.jpg" class={{checked.img}}/>
<ul>
<li class={{checked.li}}>My List Entry</li>
</ul>
</template>
Helper Code:
Template.MyTemplate.helpers({
checked() {
let liClass = '';
let imgClass = '';
//your checked condition
if(document.getElementById('myCheckBox').checked) {
let liClass = 'checked';
let imgClass = 'updatedstyle';
}
return {
li: liClass,
img: imgClass
};
}
});
That way you can bind multiple classes with different names to the checked input value.
My situation
I have a BIG list of people which all have their own profile pic and information. All items have some CSS3 styling/animations.
Im using:
<li ng-repeat="person in people">
Where people is an array with over 150 objects from an external JSON file that all have to show on the same page.
The page loads/renders slowly, and its not because of the $http GET function that gets the JSON data with people (which only takes about 100ms), but the rendering of the page and seemingly the rendering of all the css3 styling applied to each item (if i turn my CSS off it gets much faster).
My main problem
I can live with the page rendering slowly but what i really want is to detect whenever my heavy page has loaded its content in order to start and stop a loading indicator. Ive tried making a directive like this:
directive("peopleList", function () {
return function (scope, element, attrs) {
scope.$watch("people", function (value) {
var val = value || null;
console.log("Loading started");
if (val){
console.log("Loading finished");
}
});
};
});
And put this directive on the wrapper div that is loading my list of people. But the loading appears to stop whenever my object with JSON data has been filled, which only takes around 100ms, but the actual visual rendering of the page takes more like 4-5s.
Ive also tried solutions like http://jsfiddle.net/zdam/dBR2r/ but i get the same result there.
What i need
When i navigate through my pages (which all have the same kind of lists) i need something that tells me whenever the page has fully loaded and is visible for the user so that i know when to hide my loading indicator.
Try adding this directive -- the magic is in the link function:
directives
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
scope.$evalAsync(attr.onFinishRender);
}
}
}
});
Add a loader as the first item:
<li ng-show="!isDoneLoading">
<div class="loading">Loading...</div>
</li>
Then in your li, add this:
<li ng-repeat="..." on-finish-render="isDoneLoading = true;"> ... </li>