How will write jasmine to cover my function? I am getting error "config method in not exist" - jasmine

Getting error to my function which I am trying to cover in Jasmine
"Error is config method does not exist" And config the method which I am trying to cover.
I am expecting to cover the right test case scenario to my config method.
const config = () => {
return ({
name: 'modal.name',
class: 'modal.class',
choice: 'modal.choice'
});
}
describe('Config', () => {
it('config is defined', () => {
let data = {
name: 'modal.name',
class: 'modal.class',
choice: 'modal.choice'
};
Object.assign(config, data);
spyOn(data, 'config').and.returnValue(Promise.resolve(data));
});
});

You are getting that error because you are trying to spy the config method in your data object with the statement:
spyOn(data, 'config').and.returnValue(Promise.resolve(data));
The data object has no config method so, when you call spyOn it throws that error.
Your it says that you are trying to test that config is defined, but you are doing really strange things in your test. If you want to check that config is defined you could do:
it('config is defined', () => {
expect(config).toBeDefined();
});
A more elaborated test would be to test that the method, besides existing, returns the data you want:
it('config returns the configuration', () => {
expect(config()).toEqual({
name: 'modal.name',
class: 'modal.class',
choice: 'modal.choice'
});
});

Related

Function implementation showing type error in Cypress while running tests

I am working on cypress for web application automation and having an issue with accessing a function from an object.
The code below is from a javascript file in 'e2e' folder.
class productsDemo {
pageObjects = {
productName: () => cy.get('.product-name-input'),
productDescription: () => {
cy.get('.product-description-input');
},
productCode: () => cy.get('.product-code-input')
}
inputProdName(prodname) {
this.pageObjects.productName().type(prodName);
}
inputProdDesc(proddesc) {
this.pageObjects.productDescription().type(proddesc);
}
}
module.exports = new productsDemo();
The code below is from a cypress test file to import and use the class.
import productsDemo from '../pageClasses/products.js'
describe('Product Creation', () => {
it('Create new Product', () => {
cy.visit('http://localhost:3000/products');
productsDemo.inputProdName('Laptop');
productsDemo.inputProdDesc('Electronics');
productsDemo.pageObjects.productCode().should('have.text', 'XXX124');
}
})
While running this file, getting this error in test runner.
Cannot read properties of undefined (reading 'type')
And, this type error is showing in the javascript file for 'inputProdDesc' function.
If the function declaration in 'pageObjects' object from the javascript file is modified as follows, then it works without problems:
productDescription: () => cy.get('.product-description-input'),
Could anyone help me to understand what is the issue with this code in setting value for 'product description' and how and to resolve this?
productDescription: () => {
cy.get('.product-description-input');
},
You have simply missed out the return statement from your page object class method call.
This is how it should look
productDescription: () => {
return cy.get('.product-description-input');
},
Without that return statements the code thinks you are returning undefined and that is the error message it is giving you:
Cannot read properties of undefined

How do I fix a "browser.elements is not a function" error in Nightwatch.js?

I'm trying to use page objects in Nightwatch js and am creating my own commands in that. For some reason now Nightwatch doesn't seem to recognise standard commands on browser and give me a type error on different commands. What am I doing wrong with my code?
I'm tried different things here already, for example adding 'this' or 'browser' in front of the command, which didn't help. My code has gone through many versions already I am not even sure anymore what all I've tried after Googling the error.
My pageObject:
const homePageCommands = {
deleteAllListItems: function (browser) {
browser
.elements('css selector', '#mytodos img', function (res) {
res.value.forEach(elementObject => {
browser.elementIdClick(elementObject.ELEMENT);
});
})
.api.pause(1000)
return this;
}
};
module.exports = {
url: "http://www.todolistme.net"
},
elements: {
myTodoList: {
selector: '#mytodos'
},
deleteItemButton: {
selector: 'img'
}
},
commands: [homePageCommands]
};
My test:
require('../nightwatch.conf.js');
module.exports = {
'Validate all todo list items can be removed' : function(browser) {
const homePage = browser.page.homePage();
homePage.navigate()
.deleteAllListItems(homePage)
// I have not continued the test yet because of the error
// Should assert that there are no list items left
}
};
Expected behaviour of the custom command is to iterate over the element and click on it.
Actual result:
TypeError: browser.elements is not a function
at Page.deleteAllListItems (/pageObjects/homePage.js:18:14)
at Object.Validate all todo list items can be removed (/specs/addToList.js:8:14)
at processTicksAndRejections (internal/process/next_tick.js:81:5)
And also:
Error while running .navigateTo() protocol action: invalid session id
Looks like you need to pass browser to the deleteAllListItems function instead of homePage on this line:
homePage.navigate()
.deleteAllListItems(homePage)

Pass data from one step to the next synchronously

Running Cypress 3.1.1 with cypress-cucumber-preprocessor 1.5.1. I need to pass some static data from one step to another (in the same scenario/test). I can do this using an alias, like this:
cy.wrap(someString).as('myString'), but then I have to access it asynchronously:
cy.get('#myString').then(myString => ...)
This is rather cumbersome, particularly when I have to pass multiple values, requiring multiple wrapped closures, for no apparent benefit. (Currently I'm working around this by aliasing an object, but I shouldn't need to do this.)
How can I pass primitive values from one step to another synchronously?
I thought I might be able to simply set this.myString='' to set the value on the Mocha shared context object, but in that case, the property exists but is set to undefined when accessed in later steps.
Even creating my own context variable with let outside of the step definition does not work. Is this simply a limitation of Cypress and/or the cypress-cucumber-preprocessor?
I managed to get it working the following way:
Add 2 tasks to the /plugins/index.js
const testStore = {}
module.exports = (on, config) => {
on('task', {
pushValue({ name, value }) {
console.log(name, value)
testStore[name] = value
console.log(testStore)
return true
},
})
on('task', {
getValue(name) {
return testStore[name]
},
})
Then you can add a variable in any test and reach it in any other place:
it('test', ()=>{
cy.task('pushValue', { name: 'orderNumber', value: orderNumber })
})
it('test 2', ()=>{
cy.task('getValue', 'orderNumber').then((order) => {
cy.visit(`/bookings/${order}`)
})
})
Here is a slightly more complicated (and not fully tested) method. A custom command can be added to save values to a global object.
In the Cypress test runner, all the tests seem to run sequentially, but you may have to be careful if using CI and parallel execution.
In /support/commands.js
export const testStore = {}
Cypress.Commands.add('saveAs', { prevSubject: true }, (value, propName) => {
console.log('saveAs', value, propName)
testStore[propName] = value;
return value;
})
In myTest.spec.js
import { testStore } from '../support/commands.js'
...
it('should have a title', () => {
cy.title()
.saveAs('title') // save for next test
.should('contain', 'myTitle) // this test's expectation
});
it('should test something else', () => {
cy.get('.myElement').contains(testStore.title);
});

Jasmine .toHaveBeenCalledTimes throws 'undefined is not a constructor' error

I have the following unit test:
describe('shouldDeleteLogos', () => {
beforeEach(() => {
productService.product.project.parts['Logo'] = {
name: 'Logo'
};
productService.product.project.parts['Accent'] = {
name: 'Accent'
};
});
it(`should only call deleteLogos once if the part was provided to the method`, () => {
let part: Part = new Part();
part.name = 'Logo';
spyOn(creationSpacePSTLInstance, 'deleteLogos');
creationSpacePSTLInstance.shouldDeleteLogos(part);
expect(creationSpacePSTLInstance.deleteLogos).toHaveBeenCalledTimes(1);
});
});
However when I run the tests I get the following error: TypeError: undefined is not a constructor (evaluating 'expect(creationSpacePSTLInstance.deleteLogos).toHaveBeenCalledTimes(1)')
What is wrong here? I've spied on the method and I want to test that it was only called one time. Based on the documentation this should be working. It does work with just .toHaveBeenCalled();, just not with the times argument.
Here is the versions of Jasmine and Karma that I'm using.
"jasmine-core": "2.3.4"
"karma": "0.13.22",
toHaveBeenCalledTimes() was introduced in Jasmine 2.4. If you upgrade, that should fix it.

How to verify with Jasmine that a module called a sub-module method with correct arguments

The following test spec simulates calling a module that writes some content to the file system. Internally it uses fs to do that. I want to make sure fs.writeFile() was called with correct parameters. However, the toHaveBeenCalledWith() doesn't seem to work with a generic Function argument. Any ideas on how to make toHaveBeenCalledWith work as I expect?
Test Spec:
var fs = {
writeFile: function (arg1, arg2, cb){}
}
var writeContent = {
toFS: function (){
var path = "some/calculated/path";
var content = "some content";
fs.writeFile(path, content, function(){})
}
}
describe("writeContent", function() {
var writeFileSpy = null;
beforeEach(function() {
writeFileSpy = jasmine.createSpy('writeFileSpy');
spyOn(fs, 'writeFile').and.callFake(writeFileSpy);
});
it("can call spy with callback", function() {
writeContent.toFS();
expect(writeFileSpy).toHaveBeenCalledWith("some/calculated/path", "some content", Function);
});
});
Results:
Message:
Expected spy writeFileSpy to have been called with [ 'some/calculated/path', 'some content', Function ] but actual calls were [ 'some/calculated/path', 'some content', Function ].
Answering my own question :-) Just needed to enclose Function in jasmine.any() as in:
expect(writeFileSpy).toHaveBeenCalledWith("some/calculated/path", "some content", jasmine.any(Function));

Resources