How can I get a custom css variable from any element (cypress) - css

I want to create some tests checking the styles of elements. We use these custom CSS vars. Is there any way to get these in cypress instead of checking for e.g. RGB(0,0,0)?
Thx in advance!

If you use cy.should() alongside have.css, you can specify which CSS property to check, and the value.
Using a simple example from your image, it would look something like this:
cy.get('foo')
.should('have.css', 'min-width', '211px');
If there are more complex checks going on, you can always run the .should() as a callback.
cy.get('foo').should(($el) => {
const minHeight = +($el.css('min-height').split('px')[0]);
expect(minHeight).to.eql(40);
});
I found myself checking a lot of CSS values on elements, and opted to have a custom command that allowed me to pass in an expected object and check for all of those values.
Cypress.Commands.add('validateCss', { prevSubject: 'element' }, (subject, expected: { [key: string]: any }) => {
Object.entries(expected).forEach(([key, value]) => {
cy.wrap(subject).should('have.css', key, value);
});
});
const expected = { 'min-width': '211px', 'min-height': '40px' };
cy.get('foo').validateCss(expected);

Interacting with browser element or Dynamic CSS can be achieved in may ways ,
most use-full is cy.get() with the help of .should()
you can find here ( However i know you already checked this :) )
https://docs.cypress.io/api/commands/get#Get-vs-Find
for Example
cy.get('#comparison')
.get('div')
// finds the div.test-title outside the #comparison
// and the div.feature inside
.should('have.class', 'test-title')
.and('have.class', 'feature')

It is possible to evaluate a css variable fairly simply using getComputedStyle()
Cypress.Commands.add('cssVar', (cssVarName) => {
return cy.document().then(doc => {
return window.getComputedStyle(doc.body).getPropertyValue(cssVarName).trim()
})
})
cy.cssVar('--mycolor')
.should('eq', 'yellow')
where, for example
<html>
<head>
<style>
body {
--mycolor: yellow;
}
p {
background-color: var(--mycolor);
}
</style>
</head>
But asserting that <p> has --mycolor requires a dummy element to evaluate yellow to rgb(255, 255, 0).
Cypress.Commands.add('hasCssVar', {prevSubject:true}, (subject, styleName, cssVarName) => {
cy.document().then(doc => {
const dummy = doc.createElement('span')
dummy.style.setProperty(styleName, `var(${cssVarName})`)
doc.body.appendChild(dummy)
const evaluatedStyle = window.getComputedStyle(dummy).getPropertyValue(styleName).trim()
dummy.remove()
cy.wrap(subject)
.then($el => window.getComputedStyle($el[0]).getPropertyValue(styleName).trim())
.should('eq', evaluatedStyle)
})
})
it('compares element property to CSS variable', () => {
cy.cssVar('--mycolor').should('eq', 'yellow')
cy.get('p').hasCssVar('background-color', '--mycolor') // passes
cy.get('button').click() // change the css var color
cy.cssVar('--mycolor').should('eq', 'red')
cy.get('p').hasCssVar('background-color', '--mycolor') // passes
})
The complication is not really because of the CSS var, but because we are dealing with color names that are automatically translated by the browser CSS engine.
Full test page
<html>
<head>
<style>
body {
--mycolor: yellow;
}
p {
background-color: var(--mycolor);
}
</style>
</head>
<body>
<p>Some text, change the background from yellow to red.</p>
<button onclick="changeColor()">Change color</button>
<script>
function changeColor() {
document.body.style.setProperty('--mycolor', 'red')
};
</script>
</body>
</html>
Test log

Related

How to call a function inside a child component in `Vue3` created through a `v-for` loop?

I am currently building a form builder with vue3 composition API. The user can add in different types of inputs like text, radio buttons etc into the form before saving the form. The saved form will then render with the appropriate HTML inputs. The user can edit the name of the question, eg Company Name <HTML textInput.
Currently, when the user adds an input type eg,text, the type is saved into an ordered array. I run a v-for through the ordered array and creating a custom component formComponent, passing in the type.
My formComponent renders out a basic text input for the user to edit the name of the question, and a place holder string for where the text input will be displayed. My issue is in trying to save the question text from the parent.
<div v-if="type=='text'">
<input type="text" placeholder="Key in title"/>
<span>Input field here</span>
</div>
I have an exportForm button in the parent file that when pressed should ideally return an ordered array of toString representations of all child components. I have tried playing with $emit but I have issue triggering the $emit on all child components from the parent; if I understand, $emit was designed for a parent component to listen to child events.
I have also tried using $refs in the forLoop. However, when I log the $refs they give me the div elements.
<div v-for="item in formItems" ref="formComponents">
<FormComponent :type="item" />
</div>
The ideal solution would be to define a method toString() inside each of the child components and have a forLoop running through the array of components to call toString() and append it to a string but I am unable to do that.
Any suggestions will be greatly appreciated!
At first:
You don't really need to access the child components, to get their values. You can bind them dynamically on your data. I would prefer this way, since it is more Vue conform way to work with reactive data.
But I have also implemented the other way you wanted to achieve, with accessing the child component's methods getValue().
I would not suggest to use toString() since it can be confused with internal JS toString() function.
In short:
the wrapping <div> is not necessary
the refs should be applied to the <FormComponents> (see Refs inside v-for)
this.$refs.formComponents returns the Array of your components
FormComponent is used here as <form-components> (see DOM Template Parsing Caveats)
The values are two-way bound with Component v-model
Here is the working playground with the both ways of achieving your goal.
Pay attention how the values are automatically changing in the FormItems data array.
const { createApp } = Vue;
const FormComponent = {
props: ['type', 'modelValue'],
emits: ['update:modelValue'],
template: '#form-component',
data() {
return { value: this.modelValue }
},
methods: {
getValue() {
return this.value;
}
}
}
const App = {
components: { FormComponent },
data() {
return {
formItems: [
{ type: 'text', value: null },
{ type: 'checkbox', value: false }
]
}
},
methods: {
getAllValues() {
let components = this.$refs.formComponents;
let values = [];
for(var i = 0; i < components.length; i++) {
values.push(components[i].getValue())
}
console.log(`values: ${values}`);
}
}
}
const app = createApp(App)
app.mount('#app')
#app { line-height: 2; }
[v-cloak] { display: none; }
label { font-weight: bold; }
th, td { padding: 0px 8px 0px 8px; }
<div id="app">
<label>FormItems:</label><br/>
<table border=1>
<thead><tr><th>#</th><th>Item Type:</th><th>Item Value</th></tr></thead>
<tbody><tr v-for="(item, index) in formItems" :key="index">
<td>{{index}}</td><td>{{item.type}}</td><td>{{item.value}}</td>
</tr></tbody>
</table>
<hr/>
<label>FormComponents:</label>
<form-component
v-for="(item, index) in formItems"
:type="item.type" v-model="item.value" :key="index" ref="formComponents">
</form-component>
<button type="button" #click="getAllValues">Get all values</button>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script type="text/x-template" id="form-component">
<div>
<label>type:</label> {{type}},
<label>value:</label> <input :type='type' v-model="value" #input="$emit('update:modelValue', this.type=='checkbox' ? $event.target.checked : $event.target.value)" />
</div>
</script>

Programmatically style an input element's "focus" pseudo-class with Vue

So far I'm using event listeners to set the :focus pseudo-class of an input element:
const element = document.getElementById("myElementID");
element.addEventListener("focus", (e) => {
e.target.style.borderColor = "red";
});
element.addEventListener("blur", (e) => {
e.target.style.borderColor = "";
});
JSFiddle
Although this works, is there a more elegant or idiomatic way to achieve the same thing with Vue?
You don't have to listen to native events using vanilla JS syntax and getElementById when using Vue. You can specify the v-on-handler directly on the element in the template, like so:
// Vue SFC
<template>
<div>
<input #blur="doSomething" #focus="doSomethingElse" />
</div>
</template>
<script>
export default {
methods: {
// Both methods receive the same native dom event as a vanilla listener would
doSomething(e) {
e.target.style.borderColor = "red";
},
doSomethingElse(e) {
e.target.style.borderColor = "";
}
}
}
</script>
If you only want to apply this simple styling then a pure CSS solution, as provided by Manas Khandelwal, is sufficient and preferrable.
You can simply do this with CSS. Like this:
input {
outline: none;
}
input:focus {
border-color: red;
}
<input type="text" id="myElementID" />

How should I write Jest Test cases for styled component and spy on css to verify if the styles?

I have a component in React-redux, which has a PagedGrid component (basically a table which renders data row-wise).
<UsersContainer>
<Title>{t('users')}</Title>
<PagedGrid
data-auto-container="user:table"
pageData={user.data}
columns={this.column}
/>
</UsersContainer>
I have created a function for the custom styled component which applies css to the rows of the table inside PagedGrid
const UsersContainer = styled.div`
> table > tbody {
${props => customcss(props)};
}
`;
function customcss({ data = [] }) {
let styles = '';
if (data.length > 0) {
data.forEach((value, index) => {
if (value.mycondition) {
const rowStyle = `& > tr:nth-child(${index + 1}) {
background-color: ${LIGHT_BLUE}
}`;
}
});
}
return css` ${rowStyle} `;
}
Now I want to create a test case using jest to spy on the css of this table and check if the styles are getting applied or not. Can anyone help me on creating a test case for this.
Assuming that you use the #testing-library/react library, you could test your component's style by getting it directly from the html document, and see precisely what style is used for your specific element.
In your example, you can do something like below (assuming that the ${LIGHT_BLUE} value is blue):
import React from 'react';
import { render } from '#testing-library/react';
import UsersContainer from '../UsersContainer';
it('should have the background color set to blue', () => {
const { container, getAllByTestId } = render(
<UsersContainer />
);
// Replace <YOUR_CSS_SELECTOR> by your component's css selector (that can be found in the inspector panel)
let contentDiv = document.querySelector('<YOUR_CSS_SELECTOR>');
let style = window.getComputedStyle(contentDiv[0]);
expect(style.color).toBe('blue');
}
Here, to get the style of your element, I am using the element's CSS Selector. However, it could also work with the element's className, or id directly if it has one, respectively using the methods document.getElementByClassName('YOUR_DIV_CLASS_NAME'), document.getElementId('YOUR_DIV_ID') instead of document.querySelector('<YOUR_CSS_SELECTOR>'). Note that the given name here should be unique, either with the id technique, or the className.

Angular 6: How to change page background-color dynamically

I work on an Angular 6 app (with Bootstrap 4) and need to change the page background color depending on which page the user enters. Default is white, but for login and registration screen the page color needs to be blue.
What I found so far:
in ngAfterViewInit() using
this.elementRef.nativeElement.ownerDocument: this approach makes the
app more vulnerable to XSS attacks and I want to avoid that.
Set View Encapsulation to None in app.component.ts: this way I can
set the body color in the app.component, that is 1 step forward.
So, now I have in my app.component.css:
body {
background-color: blue;
}
Question:
How can I change that color value (in app.component) using a variable?
With [ngStyle] I can not reach the body background-color.
Maybe using a css variable? but how can I change the value of that css variable dynamically?
I'm new to Sass, but might this offer a solution?
My question is different from the other question on this subject as I need to be able tochange the color value dynamically.
use render2 and set class to body using document object
app.component.ts
constructor(private renderer: Renderer2) {
this.renderer.addClass(document.body, 'body-class');
}
Note: if you are toggling classes, just remove previous class before assigning new class
The way I would do it is based on the routes. When defining the routes, you can add extra data, for example a class name.
When the route changes (i.e. via navigation), the data from the active route can be used to set the class on the body tag.
This is how you can achieve this
Update the styles.css to add different classes for body:
body {
...
}
body.red {
background-color: #ff8181;
}
body.blue {
background-color: #a0c3ee;
}
Update the routes to add extra data, specifying the body class name. Add an extra data property, like bodyClass:
const routes: Routes = [
{ path: '', component: DefaultComponent },
{ path: 'red', component: RedComponent, data: { bodyClass: 'red' } },
{ path: 'blue', component: BlueComponent, data: { bodyClass: 'blue' } }
];
Write the code to read the bodyClass and set the class to the body element when navigation occurs. This can be done in the app.component.ts:
#Component({
selector: 'app-root',
template: `
<div>
<router-outlet></router-outlet>
<app-menu></app-menu>
</div>
`
})
export class AppComponent implements OnInit {
constructor(
#Inject(DOCUMENT) private document,
private renderer: Renderer2,
private router: Router,
private activatedRoute: ActivatedRoute) {
}
ngOnInit() {
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.pipe(map(() => this.activatedRoute))
.pipe(map((route) => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}))
.pipe(filter((route) => route.outlet === 'primary'))
.pipe(mergeMap((route) => route.data))
.subscribe((event) => this.updateBodyClass(event.bodyClass));
}
private updateBodyClass(customBodyClass?: string) {
this.renderer.setAttribute(this.document?.body, 'class', '');
if (customBodyClass) {
this.renderer.addClass(this.document?.body, customBodyClass);
}
}
}
Here is a demo on StackBlitz: https://stackblitz.com/edit/angular-ivy-rs1tai
Why not just define a separate class based on different background-color? For instance:
.blue {
background: blue
}
.green {
background: green
}
.grey {
background: grey
}
And then set these classes on the body using ng-class or ngClass whatever convention you use based on the page. This should be fairly easy to implement.
My favourite approach for doing stuff like this is to add a class to html tag depending on the route. For example, we have some code in our basic layout component (you could put it in your root component) that does this inside ngOnInit:
let wrapper = ''
const path = this.activatedRoute.snapshot.routeConfig.path
wrapper += this.tidyPath(path)
if (wrapper !== '') wrapper += '-'
const childPath = this.activatedRoute.snapshot.firstChild.routeConfig.path
wrapper += this.tidyPath(childPath)
this.routeWrapperCssClass = wrapper
$('html').addClass(this.routeWrapperCssClass)
This will add a class to your html tag to make it look like this (although you may have to tweak this code to suit your app):
<html class="registration">
....
</html>
The class will be instantly updated whenever you change route.
Now you can do this in your main stylesheet:
body {
background-color: pink;
}
html.registration body {
background-color: yellow;
}
You could also do things like hide elements based on the class added to the html tag like so:
.navbar {
display: block;
}
html.registration .navbar {
display: none;
}
Because you know what route you are on at all times you have total control via CSS.
PS you may want to use render2 instead of jQuery to do the DOM manipulation - see this article ... https://alligator.io/angular/using-renderer2 ... never used it myself before but almost identical to jQuery syntax - thanks to Pratap A.K answer for this
Personally i replace :
<body>
<app-root></app-root>
</body>
to
<app-root></app-root>
and then i add all the time the body on components or if i have multi
router-outlets i add it on app.component.css

MooTools 1.1, how to get id of class and apply a style

I need to get the id attribute of a class and apply a style based on that id.
So for instance, 3 list items each with the class "typo", one id is "application", another id is "application_osx", and the final id is "application_osx_terminal"
The class "typo" is handled by CSS, but I would need to assign a background image based on the ID name.
So if the id happens to be "application_osx" or "someotherid", it would automatically have this CSS applied to it
#application_osx { background: url(/path/to/image/application_osx.png) }
#someotherid { background: url(/path/to/image/someotherid.png) }
I'm trying to use MooTools 1.1 for this.
I guess it would look like this barebones
<html>
<head>
<title>Blah</title>
<script src="path/to/mootools.js"></script>
<script>
A SCRIPT THAT PRINTS OUT:
#application_osx { background: url(/path/to/image/application_osx.png) }
#someotherid { background: url(/path/to/image/someotherid.png) }
BASED ON THE CLASS "TYPO"
</script>
</head>
<body>
<ul>
<li id="application_osx" class="typo">Application OSX</li>
<li id="someotherid" class="typo">Someotherid</li>
</ul>
</body>
</html>
$$('.typo').each(function(el){
el.setStyle('background-image', 'url(/path/to/'+el.id+'.png)')
});
Should do the trick, if I understand well…
why can't you define the rules in your css file? if you need to dynamically produce rules and append to the document head then you need something like this
for mootools 1.2.x
http://www.jsfiddle.net/dimitar/G5ywF/1/
var StyleWriter = new Class({
// css classes on the fly, based on by Aaaron Newton's version
createStyle: function(css, id) {
try {
if (document.id(id) && id) return;
var style = new Element('style', {id: id||'',type:'text/css'}).inject(document.getElement('head'));
if (Browser.Engine.trident)
style.styleSheet.cssText = css;
else
style.set('text', css);
} catch(e) {
console.log("failed:", e);
}
}
});
use:
new StyleWriter().createStyle("#rule { blah; }\n#rule2 { blah... }\n", "mystyles");
edit it was just brought to my attention you are on mootools 1.11 so
http://www.jsfiddle.net/G5ywF/4/
class version for 1.11 with slight changes:
var StyleWriter = new Class({
// css classes on the fly, based on by Aaaron Newton's version
createStyle: function(css, id) {
try {
if ($(id) && id) return;
var style = new Element('style', {id: id||'',type:'text/css'}).inject(document.getElement('head'));
if (window.ie)
style.styleSheet.cssText = css;
else
style.setHTML(css);
} catch(e) {
console.log("failed:", e.message);
}
}
});
tested the 1.11 class in FF 3.6x, Chromium 5 and IE7

Resources