I'm using Playwright as my automation tool.
I've defined objects in a dedicated JS file like in the following example:
//UserObjects.js :
let userObject1 = {
section1: {
properties1: {
propery1: "property1",
},
properties2: {
property1: "property1",
property2: "property2",
},
},
section2: {
properties1 : {
property1: "property1",
},
},
sharedFunctions : {
propertyFunction : function()
{
// Implementation
},
}
}
I want to use the previously defined object in the test file: test.spec.js:
test.describe('Tests group1',async () => {
test('one', async ({ page }) => {
});
test('two', async ({ page }) => {
});
});
I want to update the same object sequentially as the tests run.
I've tried to use module.exports and try to access the object from the spec file but it treats it as undefined.
Another thing I tried is to create a copy of the object in the code block of test.describe() using :
// test.spec.js:
Before the test.describe(), I've required the object from the file:
const {userObject} = require('../userobjects/UserObjects.js');
And then created a copy of the object, but it still does treats it as undefined:
let user1 = Object.assign({}, userObject);
After performing the following in UserObjects.js :
module.exports.getEmployee = {userObject};
One more question, what practice is better? Accessing the object in the external file or creating a copy inside the test.describe()?
Thanks
Related
I am building a dictionary but I would like some of the values to contain variables. is there a way to pass a variable to the dictionary so I can assign a dot notation variable? the variables object will always have the same structure and the dictionary will be static and structured the same for each key value pair. essentially I want to pass the value from the dictionary to another function to handle the data.
main.js
import myDictionary from "myDictionary.js"
const variables ={
item:"Hello"
}
const data = myDictionary[key](variables)
console.log(data)
myDictionary.js
const myDictionary = {
key: variables.item
}
so the log should display hello. I know it willl be something straightforward but cant seem to figure it out.
as always any help is greatly appreciated
You should modify the dictionary so that it keeps actual callback functions instead. Only then it will be able to accept arguments.
const myDictionary = {
key: (variables) => variables.item
}
const variables = {
item: "Hello"
}
const key = "key";
const data = myDictionary[key](variables)
console.log(data)
What you are trying to do is not possible. The myDictionary.js file has no idea whats inside you main file. The only thing you could do would be:
myDictionary.js
const myDictionary = {
key: "item"
}
main.js
import myDictionary from "myDictionary.js";
const variables = {
item: "Hello"
};
const data = variables[myDictionary["key"]];
console.log(data);
Also, even though JavaScript does not enforce semi-colons, they will save you a lot of headaches of some stupid rule that breaks the automatic inserter.
I must apologise as when I asked the question I wasn't fully clear on what I needed but after some experimentation and looking at my edge cases and after looking at Krzysztof's answer I had a thought and came up with something similar to this -
const dict = {
key: (eventData) => {
return [
{
module: 'company',
entity: 'placement',
variables: {
placement_id: {
value: eventData.id,
},
},
},
{
module: 'company',
entity: 'placement',
variables: {
client_id: {
value: eventData.client.id,
},
},
},
];
},
}
Then I'm getting the data like this -
const data = dict?.[key](eventData)
console.log(data)
I can then navigate or manipulate the data however I need.
thank you everyone who spent time to help me
I'm trying to save and read multiple json objects from my vue-application with the vue-cookie package (Version 1.1.4). The snippet below runs really well and the json object is saved and retrivied as expected.
However, I noticed as soon as the data is retrieved via cookies and i want to change data inside the object on the web page, the "updated" lifecycle method will not trigger. This behaviour is really awkward as I am only adding the cookieToJson() method to the beforMount() method. The Vue debugger also shows that the value is changed. I think the data is not reactive anymore, but how can I fix this.
data () {
return {
json: {
a: 0,
b: 1,
},
}
},
methods: {
jsonToCookie(name) {
const data = "{\"" + name + "\": " + JSON.stringify(this[name])+"}";
this.$cookie.set(name, data, { expires: '1M' }, '/app');
},
cookieToJson(name) {
const data = JSON.parse(this.$cookie.get(name));
if(data==null) return
for(var i = 0; i < Object.keys(data).length; i++) {
var name = Object.keys(data)[i]
this[name] = data[name];
}
},
beforeMount() {
console.log("beforeMount")
this.cookieToJson("json")
},
updated() {
console.log("updated")
this.jsonToCookie("json")
},
}
In case anyone run into a similar problem, I am using the localStorage now.
Problem:
Trying to call tests within an array of items returned from a Cypress custom command.
Approaches attempted using npm package mocha-each and another test using the forEach function.
Custom Command:
I created a custom Cypress command that returns an array of AppParamsType:
/// <reference types="Cypress" />
import { AppParamsType } from 'support';
declare global {
namespace Cypress {
interface Chainable {
cmdGetAppsParams: () => Chainable<AppParamsType[]>;
}
}
}
export function cmdGetAppsParams() {
const paramsApps: AppParamsType[] = [];
cy.cmdAppKeys()
.then(($appKeys: string[]) => {
cy.wrap($appKeys).each(($appKey: string) => {
cy.cmdProviderAppParams($appKey).then((paramApp: AppParamsType) => {
paramsApps.push(paramApp);
});
});
})
.then(() => {
return cy.wrap(paramsApps);
});
}
Cypress.Commands.add('cmdGetAppsParams', cmdGetAppsParams);
Test using Custom Command:
The following Cypress test calls the custom command cmdGetAppsParams() to return an array of items.
The array is being iterated with one test using npm package mocha-each and another test using Array forEach. Neither approach calls the tests within the loops.
import * as forEach from 'mocha-each';
let apps: AppParamsType[];
describe('DESCRIBE Apps Add Apps Spec', () => {
before('BEFORE', () => {
cy.cmdGetAppsParams().then(($apps: AppParamsType[]) => {
expect($apps).to.be.an('array').not.empty;
apps = $apps;
});
});
it('TEST Apps Params Array', () => {
cy.task('log', { line: 'A', data: apps });
expect(apps).to.be.an('array').not.empty;
});
it('TEST each item mocha forEach', () => {
cy.task('log', { line: 'B', data: apps });
forEach(apps).it('item', (item: AppParamsType) => {
cy.task('log', { line: 'B.1', data: item });
expect(item).to.be.an('object').not.null;
});
});
it('TEST each item array forEach', () => {
cy.task('log', { line:'C', data: apps });
expect(apps).to.be.an('array').not.empty;
apps.forEach((item: AppParamsType) => {
it('TEST App Param', () => {
cy.task('log', { line: 'C.1', data: item });
expect(item).to.be.an('object').not.null;
});
});
});
The results I am seeing is that the outer tests, indicated by labels 'A', 'B' and 'C', are getting called. But, not the inner tests, which would be indicated by labels 'B.1' and 'C.1':
{
"line": "A",
"data": [
***
]
}
{
"line": "B",
"data": [
***
]
}
{
"line": "C",
"data": [
***
]
}
Nesting an it() inside an it() looks novel. I'm surprised you are not getting an error from it.
The basic problem when generating tests dynamically is that the Cypress runner needs to know exactly how many tests will be generated before it starts running them. But any Cypress commands (including custom commands) will not run until the entire test script has finished running (excluding callback code), so you can't get the apps list from a custom command.
The best way to proceed is to convert cy.cmdAppKeys(), cy.cmdGetAppsParams(), and cy.cmdProviderAppParams() from custom commands to a plain javascript function, and then run that function at the top of the script, e.g
const apps = getMyApps(); // synchronous JS function,
// will run as soon as the test starts
apps.forEach((item: AppParamsType) => {
const titleForTest = `Test App Param ${item.name}`; // Construct an informative title
it(titleForTest, () => {
...
})
})
If you can provide details of the custom commands cy.cmdAppKeys() and cy.cmdProviderAppParams(), may be able to help convert to the synchronous function.
I've having yeoman generator with sub generator.
I need to invoke the sub generator via code and I use the code below which is working, I see that the sub generator is invoked and I got the question in the terminal.
docs:
https://yeoman.io/authoring/integrating-yeoman.html
var yeoman = require('yeoman-environment');
var env = yeoman.createEnv();
env.lookup(function () {
env.run('main:sub',err => {
console.log('done' ,err);
});
});
The sub generator have only one question
prompting() {
const prompts = [
{
name: "app",
message: "which app to generate?",
type: "input",
default: this.props.app,
},
];
...
I want to call it silently, which means to pass the value for app question via code and not using terminal and I try this which doesn't works, (I see the question in the terminal)
env.lookup(function () {
env.run('main:sub',{"app":"nodejs"}, err => {
console.log('done' ,err);
});
});
and also tried this which doesnt works
env.lookup(function () {
env.run('main:sub --app nodejs', err => {
console.log('done' ,err);
});
});
How can I do it ? pass the values using code (maybe like it's done on unit test but this code is not unit test... when the terminal is not invoked)
From the docs im not sure how to pass the values
https://yeoman.io/authoring/integrating-yeoman.html
I've also found this but didn't quite understand how to use it to pass parameter to generator
http://yeoman.github.io/environment/Environment.html#.lookupGenerator
is it possible?
You can just do:
env.lookup(function () {
env.run('main:sub',{"app":"nodejs"}, err => {
console.log('done' ,err);
});
});
and inside the sub sub-generator, you can find the value via this.options.app.
To disable the question prompt, defined when field inside the Question Object like this:
prompting() {
const prompts = [
{
name: "app",
message: "which app to generate?",
type: "input",
default: this.props.app,
when: !this.options.app
},
];
. . .
return this.prompt(prompts).then((props) => {
this.props = props;
this.props.app = this.options.app || this.props.app;
});
}
Is it possible to reference the name of the object variable declaration within the object itself?
Something like:
const foo = {
bar: `${MAGICTHIS}-bar`,
}
console.log(foo.bar); //foo-bar
EDIT:
I am writing a dynamic function for rewriting all my css classnames to BEM in an object. I do this to be able to manage them in one point through out my application. Without the function it is like this:
export const button = {
btn: 'button',
btnSm: 'button--small',
btn2: 'button--secondary',
export const alert = {
alert: 'alert',
alertDanger: 'alert--danger',
//etc
}
They are separated in different objects because I want to isolate usage.
I wanted to optimize this since I'll be doing this a lot. That's why I'm trying to write a 'bemmify' function. So I can do this:
export const button = {
btn: bemmify(),
btnSm: bemmify('small'),
btn2: bemmify('secondary'),
export const alert = {
alert: bemmify(),
alertDanger: bemmify('danger'),
//etc
}
And have the same result as the objects above.
Of course I could always pass the 'base' as a first param (bemmify('button', 'small')) but I started to wonder if it were possible to let my bemmify function be so smart that it could recognize the name of the object it is in.
Whenever you find yourself writing code where the variable names are significant, you should generally be using an object where the variable names are keys. So you should have an object like:
const bem_data = {
button: {
btn: 'button',
btnSm: 'button--small',
btn2: 'button--secondary',
},
alert: {
alert: 'alert',
alertDanger: 'alert--danger',
}
}
Then you can use a function to create each element:
function add_element(data, key, prefix, additional) {
const obj = {
[prefix]: key
};
Object.entries(additional).forEach(([
keySuffix,
valSuffix
]) => obj[prefix + keySuffix] = `${key}--${valSuffix}`);
data[key] = obj;
}
const bem_data = {};
add_element(bem_data, "button", "btn", {
Sm: "small",
"2": "secondary"
});
add_element(bem_data, "alert", "alert", {
Danger: "danger"
});
console.log(bem_data);
Then you export bem_data and use bem_data.button, bem_data.alert, etc.