I am using open-source TestCafe for the first time. I have 3 JS dynamic variables that I need to use in the fixture but I do not know how to enter them because of the name of the variables changes.
//I have this code, the 3 variables below are dynamic and I do not know how to
//define them for the test to work
import { Selector } from 'testcafe';
fixture `Getting Started`;
.page `http://mypage/example`;
test('My First Test', async t => {
await t
.typeText('#_19ea794cf2c5da', 'John.Smith#mail.com') //e-mail
.typeText('#_1f5041dd561eb6', 'John Smith') //name
.typeText('#_1ba6e017739c70', '5515675800') //telephone
});
The connection is executed but marks error in the first dynamic variable
You need to change the way to locate elements. There are several ways to locate elements with dynamically generated IDs:
using the CSS Attribute Ends With Selector (see this answer)
using CSS selectors connected with class name, tag name and etc.
using the hierarchy selector methods (parent, child and etc.)
Related
I'm using an external library rendered using Vue3. It has the following component from a third part library [Edit: I realize the GitHub repo for that library is out of date, so updating with the actual code in my node_modules.]
<template>
<div class="socket" :class="className" :title="socket.name"></div>
</template>
<script>
import { defineComponent, computed } from "vue";
import { kebab } from "./utils";
export default defineComponent({
props: ["type", "socket"],
setup(props) {
const className = computed(() => {
return kebab([props.type, props.socket.name]);
});
return {
className
};
}
});
</script>
It renders based on a Socket object passed as a prop. When I updated the name property of the Socket, I see the title updated accordingly. However, the CSS/class does not update. I've tried $forceRefresh() on its parent, but this changes nothing.
Update: I was able to move the rendering code to my own repo, so I can now edit this component if needed.
Based on this updated code, it seems the issue is that the class is computed. Is there any way to force this to refresh?
The only time it does is when I reload the code (without refreshing the page) during vue-cli-service serve.
For reference, the | kebab filter is defined here:
Vue.filter('kebab', (str) => {
const replace = s => s.toLowerCase().replace(/ /g, '-');
return Array.isArray(str) ? str.map(replace) : replace(str);
});
Do filtered attributes update differently? I wouldn't think so.
I was also wondering if it could be a reactivity issue, and whether I needed to set the value using Vue.set, but as I understand it that's not necessary in Vue3, and it's also not consistent with the title properly updating.
Computed properties are reactive, however Vue does not expect you to mutate a prop object.
From the documentation:
Warning
Note that objects and arrays in JavaScript are passed by reference, so
if the prop is an array or object, mutating the object or array itself
inside the child component will affect the parent state and Vue is
unable to warn you against this. As a general rule, you should avoid
mutating any prop, including objects and arrays as doing so ignores
one-way data binding and may cause undesired results.
https://v3.vuejs.org/guide/component-props.html#one-way-data-flow
I know that this says, that you should not mutate it in the child, but the general rule is, that you should not mutate properties at all, but instead create new object with the modified data.
In your case the computed function will look for changes in the properties itself, but not the members of the properties, that is why it is not updating.
Within the author it displays a breadcrumb, and I know you can modify its display to either some other static text or localisation, but I'm wondering if it's possible to dynamically show an attribute, or execute some other context-specific xpath dynamically.
As a test I can change the breadcrumb using the localisation editor variable ${i18n()}.
cc_config.xml
<elementRenderings platform="webapp">
<render element="num" as="${i18n(test)}" annotation="${i18n(test)}"/>
translation-cc.xml
<key value="test">
<comment></comment>
<val lang="en_US">Year</val>
"Year" is actually a num element.
However, trying any other variable, even 'more static' ones like ${cf} or ${tp} simply render the variable text literally, instead of evaluating it.
cc_config.xml
<elementRenderings platform="webapp">
<render element="paragraph" as="${xpath_eval(./#eId)}" annotation="${xpath_eval(./#eId)}"/>
<render element="p" as="${tp}" annotation="${tp}"/>
(paragraphs do have an eId attribute)
As you can see, I tried using annotation; but these tooltips also simply display the variable literally.
I also fiddled and tried a bunch of xpath stuff, like #eId/.#eId//#eId, but I think there's some restriction in using the Content Completion Configuration File with respect to editor variables.
So is the thinking right but I am doing something wrong, or is it not the right way but there is some other way to affect the breadcrumb? Maybe with the schema?
The element display names in cc_config.xml file do not support most of the editor variables. Most of them, like ${cf} (current file) and ${tp} (total number of pages) don't make sense to be used when rendering the name of an element.
The xpath_eval would make sense - the display name of an element may depend on its attributes (e.g. the #id attribute), it's index in the document (e.g. 'Section 3'), etc. We have a feature request registered for this case and I added your vote to it.
As a partial workaround you can use a JS API to compute the display name of the element based on the element original name and its attributes:
goog.events.listen(workspace, sync.api.Workspace.EventType.BEFORE_EDITOR_LOADED, function(e) {
e.options.elementNameEnhancer = function(elemName, attrs) {
var displayString = elemName;
var attr = attrs['id'];
if (attr) {
displayString += ' (#' + attr.attributeValue + ')';
}
return displayString;
};
});
We are using React+Redux and it's doing well. But there is one situation I never know which strategy to use.
When I need to loop over a collection, I could write:
Pass the element
Code:
render() {
collection.map(element => <ElementItem key={element.id} element={element} />)
}
Pass the spread element
Code:
render() {
collection.map(element => <ElementItem key={element.id} {...element} />)
}
Pass the ID
Code:
render() {
collection.map(element => <ElementItem key={element.id} id={element.id} />)
}
and in ElementItem.js:
connect((state, ownProps) => {
element: state.collection.find(_el => _el.id === ownProps.id)
})(ElementItem)
All in one file:
Code:
render() {
collection.map(element => <li key={element.id}><p>{element.name}</p></li>)
}
Solution #4 is not reusable so not much interesting.
I don't like solution #2 since attributes are drowned in others
I find #3 to be the cleanest since it is the one with lesser dependencies and forwarded props. The biggest trade off is that it feels lame to launch a .find for each ElementItem
So I guess it is the first one which wins. But I have the feeling this is not the redux-way of doing things, is it? If I pass the element parameter, why wouldn't I pass more? Then we are loosing all the benefits of isolating container from presentation components, don't we ?
Solutions #1 and #2 are perfectly fine, because in that case ElementItem is a presentational component and received its data from props.
Solution #3 makes no sense, because the component looping over the collection already got the collection part of the state (either because this component is connected to Redux, or because it got it from props).
In the redux documentation, there is an example where they render a collection of todos: They use 2 presentational components: Todo, a single todo item, and TodoList, a list showing todos. Then there is a container component, VisibleTodoList, which computes the list of visible todos from the state and display them using TodoList. You could use the same strategy when you want to loop over a collection.
Another point: if you don't want to use find to get the right item, you could normalize your state, so each 'collection table' stores the items in an object with the ids of the items as keys. This way, you could get the right item like this:
const element = state.collection[elementId];
I am a beginner with AngularJS and I have a little problem, I installed grunt-contrib-less to support less files instead css but now I have to declare all less styles that will be compiled into only one css file.
But my problem is normally when I'm using less, I write some code for a specific page, and here I have to write the style code for all pages. This is confusing and not really maintanable so is there a best practice to organize less styles?
I tought that there may be multiple solution:
Apply a class to body tag and change it with I don't know what
(controller, services or other)
(Import LESS file only for one page)
Generate multiple css file depending which style is compiled (but I can't do this because I can't configure grunt correctly)
Do this with DOM manipulation (but it don't find it beautifull because I think Angular must have a good way to solve that problem)
Could you explain me how to have different style for differents views ? I don't want to have the same style for all links in all views and without create hundreds classes I don't know how to do that.
Use directive
and add whatever variables/code/logic you want to add
HTML template(directive) of style can be added to your view and after compile you will get different ui for all your views
for reference read
angular directive
I solve my problem by adding specific class on body tag depending the route.
I put a variable in rootScope called 'pageStyle' with correspond to the classname that I want. This variable is updated automatically when route change (see run function). There is an event when the route change ($stateChangeSuccess or $routeChangeSuccess depending if you are using ngRoute or -angularui-routeur).
In my case i would like to add the name of the route but you can do it with the controller name or something else.
Here is an example
This is the routes :
angular
.module('frontApp', [])
.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider, $mdThemingProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: '../views/home.html',
controller: function ($scope) {
$scope.msg = 'Xavier';
}
})
.state('form', {
url: '/form',
templateUrl: '../views/form.html',
controller: 'FormCtrl'
});
}])
And in the run function you will see the event bound to adapt the class when route change :
.run(function($rootScope) {
$rootScope.pageStyle = '';
// Watch state and set controller name in pageStyle variable when state change
$rootScope.$on('$stateChangeSuccess', function(event, toState) {
event.preventDefault();
if (toState && toState.name && typeof toState.name === 'string'){
$rootScope.pageStyle = toState.name;
} else {
$rootScope.pageStyle = '';
}
});
});
Extra informations :
Note that the event called when route change is different if you are using ngroute. use "$routeChangeSuccess" if you use ngRoute and "$stateChangeSuccess" if you choose to use angular-ui-routeur
If you want to add the controller name instead the route name simply use the follow and replace 'ctrl' with you controller suffixe:
if (toState && toState.controller && typeof toState.controller !== 'function'){
$rootScope.pageStyle = toState.controller.toLowerCase().replace('ctrl','');
}
Hope it help someone else
I need to load and apply CSS at runtime in my Flex app. I know that the adobe docs say that you need to compile the CSS before loading it but I would like to find a work around.
I know that you can set individual styles like this:
cssStyle = new CSSStyleDeclaration();
cssStyle.setStyle("color", "<valid color>);
FlexGlobals.topLevelApplication.styleManager.setStyleDeclaration("Button", cssStyle, true);
I was planning on parsing a CSS file and appling each attribute as above.
I was wondering if:
Adobe had a CSS parser library that I could use
Someone else had a CSS parser that I could use
If I write my own CSS parser what I should watch out for
I know that the adobe flex.text.StyleSheet class has a CSS parser but I could not find a way to harness that. (Is there a way to get that source code?)
Edit: This solution does not work. All selectors that are taken out of the parser are converted to lowercase. This may work for your application but it will probably not...
I am leaving this answer here because it may help some people looking for a solution and warn others of the limitations of this method.
Although it was not intended for this it is possible to use the StyleSheet class to parse the CSS. I am currently investigating how robust this is currently but for the most part it appears to be working.
public function extractFromStyleSheet(css:String):void {
// Create a StyleSheet Object
var styleSheet:StyleSheet = new StyleSheet();
styleSheet.parseCSS(css);
// Iterate through the selector objects
var selectorNames:Array = styleSheet.styleNames;
for(var i:int=0; i<selectorNames.length; i++){
// Do something with each selector
trace("Selector: "+selelectorNames[i];
var properties:Object = styleSheet.getStyle(selectorNames[i]);
for (var property:String in properties){
// Do something with each property in the selector
trace("\t"+property+" -> "+properties[property]+"\n");
}
}
}
I had similar problem but more precisely i want the completely avoid the compilation because my application is wrapper by ActiveX used by a custom exe file and i let the software distributor to customize their skin.
In practice we put the <fx:Style> outside the application. To avoid low level parsing on the string we had transformed the Style Sheet in an XML:
<styles>
<namespace name="myNs" value="com.myComponent">
<declaration selector="myNS|Button#myselector:over #mysubselector">
color:#ffffff;
font-size:bold
</declaration>
... other styles
</styles>
Beside the security considerations about let the user know your components you can load the XML and create a CSSStydeclaration.
Splitting and parsing only the selector let you create a series of CSSCondition and CSSSelector to add to your CSSStyleDeclaration. To parse the selector we use a little loop which search "#",":" and "." and split the string mantaining the sequence of the found CSS conditions.
var selectors:Array = [];
// first selector
var conditions:Array = [
new CSSCondition(CSSConditionKind.ID, 'myselector');
new CSSCondition(CSSConditionKind.PSEUDO, 'over');
];
// here you have to find and expand the namespace
ancestor:CSSSelector = new CSSSelector('com.myComponent.Button', conditions);
selectors.push(selector);
// second selector
var conditions:Array = [
new CSSCondition(CSSConditionKind.ID, 'mysubselector');
];
selector:CSSSelector = new CSSSelector('', conditions, ancestor);
selectors.push(selector);
// Empty style declaration
new CSSStyleDeclaration(selectors, styleManager, false);
Then you can parse CSS properties by parseCSS() with the function created by #sixtyfootersdude, but using a fake selector:
var myCSS:String = "#fake " + "{" + cssTextReadedFromXML + "}";
var style:StyleSheet = new StyleSheet();
sheet.parseCSS(myCSS);
// here you have your parsed properties
var list:Object = sheet.getStyle('#fake');
Then you can add the properties to the CSSStyleDeclaration and apply them by the setStyle method and apply the declaration as in your example.
Less or more is how I've tryed to resolve this.