I am trying to set up on my cypress framework a way to, on every test scenario, execute some actions, among them a cy.request.
My beforeEach.js file is somewhere along these lines:
import { Actor, Action } from "cypress-screenplay";
import * as auth from "../../../../../support/ui/auth";
new Actor().perform(auth.uiLogin);
(the Actor object ultimately executes a cy.request) to perform a log in via API).
Then with the simplest test, I get the following message:
The following error originated from your test code, not from Cypress.
> Cannot call cy.request() outside a running test.
This usually happens when you accidentally write commands outside an it(...) test.
If that is the case, just move these commands inside an it(...) test.
How else can I solve this problem?
I do not want to include the contents of the beforeEach on another step of my cucumber test as it would add quite a lot of noise (one line per test, on 100 tests...)
beforeEach() is a hook - a function call - not a file. In /cypress/support/index.js add
import { Actor, Action } from "cypress-screenplay";
import * as auth from "./ui/auth";
beforeEach(() => {
new Actor().perform(auth.uiLogin)
})
This will run your login before every test.
One problem though, the usage for screenplay shows
const actor = new Actor();
at the top of the test, and the actor logged in should be the same actor tested.
You can perhaps use a custom command instead,
/cypress/support/index.js
import { Actor, Action } from "cypress-screenplay";
import * as auth from "./ui/auth";
Cypress.Commands.add('loginNewActor', () => {
const actor = new Actor()
actor.perform(auth.uiLogin)
return actor
})
test
let actor
beforeEach(() => {
actor = cy.loginNewActor()
})
Related
I have a Cypress test that has only one testing case (using v10.9 with the test GUI):
describe("Basic test", () => {
it.only("First test", () => {
cy.visit("http://localhost:9999");
cy.pause();
//if(cy.get("#importantBox").contains(...) {
//}
//else
{
Cypress.runner.stop();
console.log("Stopping...");
}
console.log("Visiting...");
cy.visit("http://localhost:9999/page1");
If a certain element doesn't exist in the page, I don't want the test to continue, so I try to stop it.
Unfortunately, I can see this in the console:
Stopping...
Visiting...
And the test keeps going without the necessary data...
So, can I somehow stop it without using huge if statements?
Stopping the test is relatively easy, the harder part is the condition check.
Cypress runner is built on the Mocha framework, which has a .skip() method. If you issue it in your else clause and inside the Cypress queue, the test will stop.
Two ways to access skip():
Using a function() callback gives access to this which is the Mocha context
it('stops on skip', function() {
...
cy.then(() => this.skip()) // stop here
})
Use cy.state() internal command (may be removed at some point)
it('stops on skip', () => {
...
cy.then(() => cy.state('test').skip()) // stop here
})
You should be aware that all Cypress commands and queries run on an internal queue which is asynchronous to javascript code in the test like console.log("Visiting..."), so you won't get any useful indication from that line.
To use synchronous javascript on the queue, wrap it in a cy.then() command as shown above with the skip() method, so to console log do
cy.then(() => console.log("Visiting..."))
I use a React component with Apollo client as function component. The function body of my main search component looks like this:
function SearchContainer(props) {
const [begTime, setBegTime] = useState('')
const runSearch(begin_time) {
console.log('begin_time: ', begin_time) <== The printed value is Ok!
setBegTime(begin_time) <=== Use hook to set the value of begTime
console.log('begTime: ', begTime) <=== The output shows no change in begTime. Why?
}
return (
// Use SearchForm to get the search parameters.
<SearchForm
handleSearch={runSearch} <== Use SearchForm to get begin_time back into this component.
/>
// Provide the parameters from SearchForm and run the useQuery from Apollo using this parameters.
<SearchResults
begTime={begTime}
/>
)
}
The SearchForm is just a usual form as a React function component with useState hooks and calls on form submit the hanldeSearch function.
function SearchForm({handleSearch}) { <== handleSearch function from the parent component.
const handleSubmit = (begin_time) => {
handleSearch(begin_time) <== This call works and the value will be provided to the parent.
}
...
}
My idea of this code is to create 2 independent components. One component (SearchForm) should get the parameters. The other component (SearchResults) will get this parameters as arguments, run the query using useQuery and show the results.
But the useState hook does not work very well in my case. Interesting enough if I call the corresponding search form twice, I can see in the runSearch function, that the begTime has got the previous search values and not the initial value. So apparently the useState kind of works, but I want to run the search with the current values and not with the previous ones.
Is it possible at all to create such components with React hooks? It's my first big test with hooks instead of classes.
Thanks in advance
Andrej
For your question
const runSearch(begin_time) {
console.log('begin_time: ', begin_time)
setBegTime(begin_time)
console.log('begTime: ', begTime) <=== The output shows no change in begTime. Why?
}
The output shows no change in begTime. Why?
As stated in docs when we set state that is async function.
By that i mean your code will keep running and to set state react will start child process on another thread. And when its complete it will pass result to main thread. ( that you can c in useEffect or componentDidUpdate for early version).
So Main points are
at setBegTime(begin_time) async process is started
at main thread code will not wait for it
So next statement that is console.log('begTime: ', begTime) is processed and u saw no changes as in actual its value is not updated yet. React is still updating value.
Updating process is async because React dont want to main thread to wait for heavy work ( updating state is heavy process ) as if it wait then webpage will not respond untill it is completed. So it instead run that process on another thread.
For second one
you can try this
function SearchContainer(props) {
const [begTime, setBegTime] = useState('')
const [response,setResponse] = useState({})
useEffect(()=>{
const runSearch = (begin_time) {
setBegTime(begin_time)
}
},[begin_time])
// u can rather define working of handleSubmit in parent component and <br/>
// store its output in state and then pass it to another component
const handleSubmit = (begin_time) => {
resp = handleSearch(begin_time)
setResponse(resp)
}
return (
// Use SearchForm to get the search parameters.
<SearchForm
handleSearch={()=>handleSubmit()}
/>
// Provide the parameters from SearchForm and run the useQuery from Apollo using this parameters.
<SearchResults
begTime={begTime}
response={response}
/>
)
}
What is the point of only() and skip()? If I only want to have a single it/describe get executed, why should I keep the other things in the file? If I want to skip something, why shouldn't I just remove that code? When does one want to use those methods?
About only. Imagine that you have 2000 unit tests in some npm module. And you need write 3 more tests for new feature. So you create something.test.js file and write test cases with describe.only()
const assert = require('assert')
describe.only('sample class', () => {
it('constructor works', () => {
assert.deepEqual(true, true)
})
it('1st method works', () => {
assert.deepEqual(true, true)
})
it('2nd method works', () => {
assert.deepEqual(true, true)
})
})
Now if you launch test locally via npm test, you run only your 3 tests, not whole bunch of 2003 tests. Tests are written much faster with only
About skip. Imagine that you need to implement urgent feature in 20mins, you don't have enough time to write tests, but you have time to document your code. As we know unit tests are the best documentation, so you just write test cases how code should works with describe.skip()
describe.skip('urgent feature', () => {
it('should catch thrown error', () => {})
})
Now everyone in your team know about your new functionality and maybe someone write tests for you. Now the knowledge of how your new feature works is not only in your head, the whole team knows about it. It's good for the project and business.
more reasons to use skip
I am using react-redux 5.0.6 and have a reducer with the following code:
export default (state = [], action) => {
switch(action.type) {
case 'ADD_ENGAGEMENT':
let newArr = state.slice();
newArr.push(action.payload);
return newArr;
case 'UPDATE_ENGAGEMENT':
console.info('UPDATE_ENGAGEMENT')
return state.slice();
// return state;
default:
return state;
}}
The issue occurs within the 'UPDATE_ENGAGEMENT' case -- the actual logic has been removed and replaced with the simplest example to demonstrate the problem.
When a new array created from state via state.slice() is returned, a loop is triggered, causing the action to be dispatched until an 'Uncaught RangeError: Maximum call stack size exceeded' error is raised. Screenshot of the browser console during the issue's occurrence
The issue is not limited to 'slice()' and occurs whenever an array containing any element of state is returned e.g. return [state[0]].
When the original state is returned, the issue does not occur.
I am completely baffled by this behavior and cannot fathom how anything in my application could be causing it. Any insight would be immensely appreciated.
To provide some additional context, below is the code involved in the action's being dispatched:
componentWillReceiveProps(newProps) {
let engagementTemplateData = newProps.selectedEngagementTemplate;
let engagements = newProps.engagements;
if (engagementTemplateData && engagementTemplateData.engagementUuid === this.props.uuid) {
let template = engagementTemplateData.template;
this.updateEngagementTemplate(template);
}
}
updateEngagementTemplate(template) {
let url = `/engagements/${this.props.uuid}`;
let requestHelper = new AjaxRequestHelper(url);
let data = {template_uuid: template.uuid};
this.props.updateEngagement({uuid: this.props.uuid, template: template});
// requestHelper.put(data, response => {
// this.props.updateEngagement({uuid: this.props.uuid, template: template});
// });
}
Basically, the function which triggers the action is called in componentWillReceiveProps as a result of another action. However, I am not sure how helpful this information is, since the reducer itself appears to be working properly when responding to the action -- it's just that something strange is happening with the state, which prevents its elements from being returned.
From the sounds of it (and from the react callstack), I imagine the array changing (by reference) in the store is being picked up by a react component props, which in its should/did update logic is calling that action without a guard. This is often a mistake when calling actions or setState from componentDidMount/Update -
It works when the original state is returned as the reference is the same so React does not continue with its update logic, and hence call your code that publishes the action
Consider this pure component that will cause an endless loop with your reducer code...
export interface IMyProps {
myArray: any[],
updateEngagementAction: () => void
}
export class EndlessLoopFromArrayPropComponent extends React.PureComponent<IMyProps> {
// PureComponent compares props by reference only
// so will cause update if this.props.myArray reference has changed in store (ie from slice())
render() {
// blahblah...
}
componentDidUpdate() {
// this will call action after every update
// as every time this action is called it passes in a new reference this.props.myArray to this component
// so react will update this component again, causing the action to be called again
// ... endless loop
this.props.updateEngagementAction()
}
}
Your implementation will differ of course but this will be the principal that is causing it to happen, so you need to add a guard condition in whatever code path leads to your action being called.
For the code above you would need to check an escape condition before sending the action OR implement shouldComponentUpdate or similar to do deeper prop comparison to guard against unnecessary updates, and hence it would not reach that action code in the componentDidUpdate method
EDIT This was written before the react code was added to question. Here I refer to the action being called without guard in componentDidUpdate however the same applies when called in any of the other lifecycle methods triggered by a prop change, in this case componentWillRecieveProps. To be fair it did have a guard already, but never returned false as a more in-depth props check was needed, so caused a loop to occur via willreceive -> true -> action -> reducer -> willreceive -> true ........
I want to retrieve a list of elements on a page, and for each one, create a test spec. My (pseudo) code is :-
fetchElements().then(element_list) {
foreach element {
it("should have some property", function() {
expect("foo")
})
}
}
When I run this, I get "No specs found", which I guess makes sense since they are being defined off the main path.
What's the best way to achieve dynamically created specs?
There are major problems preventing it to be easily achieved:
the specs you are creating are based on the result of asynchronous code - on the elements Protractor should first find
you can only have the Protractor/WebDriverJS specific code inside the it, beforeEach, beforeAll, afterEach, afterAll for it to work properly and have the promises put on the Control Flow etc.
you cannot have nested it blocks - jasmine would not execute them: Cannot perform a 'it' inside another 'it'
If it were not the elements you want to generate test cases from, but a static variable with a defined value, it would be as simple as:
describe("Check something", function () {
var arr = [
{name: "Status Reason", inclusion: true},
{name: "Status Reason", inclusion: false}
];
arr.map(function(item) {
it("should look good with item " + item, function () {
// test smth
});
});
});
But, if arr would be a promise, the test would fail at the very beginning since the code inside describe (which is not inside it) would be executed when the tests would be loaded by jasmine.
To conclude, have a single it() block and work inside it:
it("should have elements with a desired property", function() {
fetchElements().then(element_list) {
foreach element {
expect("foo")
})
}
}
If you are worried about distinguishing test failures from an element to element, you can, for instance, provide readable error messages so that, if a test fails, you can easily say, which of the elements has not passed the test (did not have a specific property in your pseudo-test case). For example, you can provide custom messages to expect():
expect(1).toEqual(2, 'because of stuff')
We can generate dynamic tests using jasmin data provider but it is working with only static data.
If we want to generate tests from the asynchronous call in protractor, then we need to use onprepare function in the protractor config js.
Create a bootloader and read the test cases from excel or server and import the data loader in the onprepare function. It is bit difficult to explain because I have faced
many issues like import is not supported in this javascript version and expected 2 args but got only 1. finally I have used babel to fix the issues and able to generate tests.
Below is the sample implementation that I have done in the on prepare method
var automationModule = require('./src/startup/bootloader.ts');
var defer = protractor.promise.defer();
automationModule.tests.then(function(res) {
defer.fulfill(res);
});
bootloader.ts contains the code to read the test suites and tests from excel sheet and sets the tests to the single to class.
Here res is the instance of singleton class which is returning from bootloader.ts
hard to explain everything here but you can take a look at my full implementation in my github https://github.com/mannejkumar/protractor-keyword-driven-framework