I'm starting to use cypress and I wanted to do 2 test. One to verify what is displayed if my api return 'false' and one to what is on screen if my api return 'true'.
I tried to do a simple test like this one :
context('contextTest', () => {
before(() => {
cy.waitLoading();
});
beforeEach(() => {});
it('false test', function() {
cy.intercept('POST', '**/test/alreadySent', {
fixture: 'test/alreadySent-false.json',
}).as('alreadySent');
cy.wait('#alreadySent');
cy.get('[data-cy=alreadysent-button]');
});
});
But the intercept doesn't work and it always return the true api call.
What is strange is, if I just put the code in my before(), all work fine as expected.
context('contextTest', () => {
before(() => {
cy.intercept('POST', '**/test/alreadySent', {
fixture: 'test/alreadySent-false.json',
}).as('alreadySent');
cy.waitLoading();
});
beforeEach(() => {});
it('false test', function() {
cy.wait('#alreadySent');
cy.get('[data-cy=alreadysent-button]');
});
});
But I need to change the intercept for the next test so I wanted to set the intercept on this test exclusively.
Is it possible, why is my first code doesn't seem to work?
Or should I write my next test on another file and it is a bad practice to do this kind of verification on the same file?
Since it works when the intercept is moved up in the command order, it seems that cy.waitLoading() triggers the POST and not cy.get('[data-cy=alreadysent-button]').
The intercept must always be set up before the trigger (page visit or button click).
But the intercept varies between tests, so instead of before() I would try setting up a helper function that is called at the top of each test.
const loadAndIntercept = (apiResult) => {
const apiFixture = apiResult ? 'test/alreadySent-true.json' : 'test/alreadySent-false.json';
cy.intercept('POST', '**/test/alreadySent', { fixture: apiFixture }).as('alreadySent');
cy.waitLoading();
})
it('false test', function() {
loadAndIntercept(false);
cy.wait('#alreadySent');
cy.get('[data-cy=alreadysent-button]');
});
it('true test', function() {
loadAndIntercept(true);
cy.wait('#alreadySent');
cy.get('[data-cy=alreadysent-button]');
});
This should work since intercepts are cleared between tests. Ref docs - intercept
Note: all intercepts are automatically cleared before every test.
Related
I manage to run CYPRESS without any worries on a site without authentication.
But on an intranet, I can't identify myself. I must to log in before.
Here is my code:
describe('home', () => {
it('home accessible', () => {
cy.visit('/')
})
//We fill the login FORM
it('User Field', () => {
cy.get('input#user')
.type('login')
})
it('User pass', () => {
cy.get('input#pass')
.type('mot de passe')
})
it('check consent', () => {
cy.get('input#permalogin')
.click({ force: true })
})
it('submit', () => {
cy.get('input.btn.btn-primary')
.click()
})
//the form is submit, we can visit a page
it('autre page!!', () => {
cy.visit('/luniversite/page-2',{ timeout: 30000 })
})
//We check the title of the page, we should be on the page 2
it('titre page 2', () => {
cy.title().should('eq', 'page 2: INTRANET)
})
CYPRESS and the CYPRESS video show me that I am blocked on the authentication page.
The test on the title of the page is not correct, I don't access page-2. I stay on the first page for log in.
First thing's first: This appears to be one test, but you are specifying multiple it() functions, which is breaking it up into multiple tests, which is not what you want. You will want to restructure your test like this:
describe("home", () => {
it("home accessible", () => {
cy.visit("/");
//We fill the login FORM
cy.get("input#user").type("login");
cy.get("input#pass").type("mot de passe");
cy.get("input#permalogin").click({ force: true });
cy.get("input.btn.btn-primary").click();
cy.visit("/luniversite/page-2", { timeout: 30000 });
cy.title().should("eq", "page 2: INTRANET");
});
});
With that out of the way, it's hard to know what your application is doing without more details:
1/ When executed manually, is your application authenticating properly with the provided credentials? Do you have console errors? Have you determined that the element locators you're using are actually interacting with the elements in the manner you expect?
2/ Is your test attempting to navigate to /luniversite/page-2 before authentication is complete? If so, you may want to use intercept your authentication call and wait for it to complete:
// get your authentication POST request from network tab of devtools and use that in the cy.intercept call
cy.intercept('POST', '/yourAuthenticationCallUrl').as("#authenticationCall")
// YOUR LOGIN STEPS HERE
cy.wait("#authenticationCall") //waits for the authentication call to complete before moving to the next step
cy.visit("/luniversite/page-2", { timeout: 30000 });
I'm trying to do a pretty simple intercept in Cypress using a Vue's application. My component has a setup method using render function as such:
setup() {
useInfiniteLoading({ runner: ... })
}
Then on my tests I do the following:
describe("List todo resource", () => {
it("Checks it loads more todos when scrolling to the bottom", function () {
cy.intercept('/todo').as('getTodos');
cy.visit("/todos");
cy.wait("#getTodos").then(({response}) => {
console.log(response);
})
})
})
When running the test I see that the intercept is not stubbing the response.
As you can see from the image the request makes a request to my actual server running locally and the response is stubed. The weird part is that in a previous test I have:
it("Checks the todo list gets updated when clicking on to resolve it (from true to false)", function () {
cy.visit("/todos");
const resolved = false;
const shouldHaveClass = resolved
? "mdi-checkbox-marked-outline"
: "mdi-checkbox-blank-outline";
cy.intercept("GET", "todo", {
fixture: "resources/todo/list.todo.json",
}).as("getTodos");
cy.intercept("PUT", "todo", {
body: { data: { ...this.updateTodoFixture.data, resolved } },
}).as("updateTodo");
cy.get(".todo-list-item__resolve")
.first()
.each((btn) => {
btn.click();
});
cy.get(".todo-list-item__resolve")
.first()
.should("satisfy", ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes(shouldHaveClass);
});
});
And the response is stubbed using intercept as you can see from the previous screenshot. Is it possible that the previous test is affecting the next test? I have tried taking a look into "Intercept too soon" but no luck on trying to apply the fix described in the page.
Any idea on what could be causing the stub not to happen?
So I have a flaky test case right now in Cypress. When the modal loads, it skips the filling out the name part but fills out everything else. Therefore, causing it to fail the first time, but on the retry, it passes. I tried adding an assertion (see below), but it does not work. I am trying to avoid adding using a wait command, but wanted to see if there are any other alternatives out there to cy.wait().
describe("Coach dealing with forms" () => {
beforeEach(() => {
loginfile.login(users.coach)
});
it("Can fill out form and submit", () => {
cy.get("#modal-button").click()
cy.get("#modal-form").should("exist")
cy.get("#form-name").type("Mike Johnson")
cy.get("#form-age").type("33")
cy.get("#form-email").type("mj09#yahoo.com)
cy.get("#submit-button").click()
}
}
There a number of ways to approach flaky tests. You can try these and check what suits best for your use case:
1.Test Retires easy and effective, just retry the execution automatically it something fails. Can be applied, globally using cypress.json or to a describe block or to a it block as well.
For it block:
// `it` test block with custom configuration
it('allows user to login', {
retries: {
runMode: 2,
openMode: 2
}
}, () => {
// ...
})
For describe block:
// Customizing retry attempts for a suite of tests
describe('User bank accounts', {
retries: {
runMode: 2,
openMode: 2,
}
}, () => {
// The per-suite configuration is applied to each test
// If a test fails, it will be retried
it('allows a user to view their transactions', () => {
// ...
}
it('allows a user to edit their transactions', () => {
// ...
}
})
2.Adding .visible and click() before writing into the name field
it("Can fill out form and submit", () => {
cy.get("#modal-button").click()
cy.get("#modal-form").should("exist")
cy.get("#form-name").should('be.visible') //making sure the name field is visible
cy.get("#form-name").click() //clicking on the name field
cy.get("#form-name").type("Mike Johnson")
cy.get("#form-age").type("33")
cy.get("#form-email").type("mj09#yahoo.com")
cy.get("#submit-button").click()
})
3.Use timeouts instead of waits
cy.get("#form-name", {timeout: 10000}).type("Mike Johnson")
4.Use cy.intercept() to wait for the XHR request to finish execution. Here I am assuming that there is some unfinished XHR request after clicking the modal button that is causing the issue. If that is not the case, you can debug your tests to find out and apply intercept accordingly.
it("Can fill out form and submit", () => {
cy.intercept('http://example.com/settings').as('getSettings') //Intercept the url that is triggered after clicking the modal button
cy.get("#modal-button").click()
cy.wait('#getSettings') //Wait till that request has finished execution
cy.get("#modal-form").should("exist")
cy.get("#form-name").type("Mike Johnson")
cy.get("#form-age").type("33")
cy.get("#form-email").type("mj09#yahoo.com")
cy.get("#submit-button").click()
})
You should add a timeout to the type() command (not to the get() command) and follow it with a should() to confirm the value has been entered.
type() and should() will both retry until they succeed.
it("Can fill out form and submit", () => {
cy.get("#modal-button").click()
cy.get("#modal-form") // .should("exist") - don't need as cy.get() does that already
cy.get("#form-name").type("Mike Johnson", {timeout: 10000}) // wait for actionability
.should('have.value', 'Mike Johnson');
cy.get("#form-age").type("33")
cy.get("#form-email").type("mj09#yahoo.com")
cy.get("#submit-button").click()
})
It works fine for the same click event if I've coded within the single 'it' block as below.
Working code:
describe('Test Suite', () => {
it('Test case 1', () => {
//Test 1
//Test 2
})
})
Not working:
describe('Test Suite', () => {
it('Test case 1', () => {
//Test 1
})
it('Test case 2', () => {
//Test 2
})
})
Below is my code snippet, First 'it' block works fine after login method executes. Then second it blocks just clicking the right element but the page never loads.
P.S. If I written the code under the single 'it' block, Page loads and works fine.
describe('Fund Manager Suite', () => {
//Checking Fund Manager page loading
before(() => {
cy.visit('xxxxxxxxxxxxx')
cy.login('xxxxx', 'xxxxx')
})
it('fund manager navigation works', () => {
cy.location('pathname').should('equal', '/xxxxx')
cy.get('#appSwitcher').click()
cy.get('#appSwitcher > .dropdown > .dropdown-menu > :nth-child(2) > a').click()
cy.location('pathname').should('equal', '/xxxxx')
cy.get('.k-grid-table').find('tr').should('have.length', 5)
})
it('fund detail works', () => {
cy.get('.product > :nth-child(2)').click()
cy.location('pathname').should('equal', '/xxxxx')
// Fund Detail - Search
cy.get('#s2id_autogen31').type('Rach')
cy.get('#select2-result-label-32').click()
cy.get('#searchSubmit').click()
cy.get('#DataTables_Table_0').find('tr').should('have.length', 10)
})
})
Execution Screen shot
Code snippet screen shot
You have to preserve your cookies in beforeEach() to make sure that you stay logged in, in all it() blocks. You can read more in cypress docs.
describe('Dashboard', () => {
before(() => {
// log in only once before any of the tests run.
// your app will likely set some sort of session cookie.
// you'll need to know the name of the cookie(s), which you can find
// in your Resources -> Cookies panel in the Chrome Dev Tools.
cy.login()
})
beforeEach(() => {
// before each test, we can automatically preserve the
// 'session_id' and 'remember_token' cookies. this means they
// will not be cleared before the NEXT test starts.
//
// the name of your cookies will likely be different
// this is an example
Cypress.Cookies.preserveOnce('session_id', 'remember_token')
})
it('displays stats', () => {
// ...
})
it('can do something', () => {
// ...
})
it('opens a modal', () => {
// ...
})
})
I Have seen this kind of behavior, when the first it case start and doesn`t finish some xhr request in the site - and in result cypress continues the process during the start of the second it case. The best solution for clean slate for each case I had found is to separate every case in different file
Update:
Able to resolve this issue by storing the session_id.
Cypress.Cookies.defaults({
preserve: "session_id"
})
I'm trying to test out an imported static method, that ought be run on button click. Here is the component, on which the test ought be run:
const NextButton = (props: ISubmitButtonProps) => (
<button
onClick={Service.goToNext}
className="col-2 submit"
type="submit"
disabled={props.submitting}>
Next
</button>
);
export default NextButton;
Now, I want to test whether the method goToNext is actually being run.
For that I've used an example from here and have added a mock looking fine, like this:
const goToNextMock = jest.fn();
jest.mock('Services/service', () => ({
default: class {
public static goToNext() {
goToNextMock(); //the jest.fn from above
}
}
})
);
And later wrote the test for it, simply simulating the click event, and then checking if the jest.fn() was run:
it('should call goToNext on button click', () => {
//button is set before as shallow(<NextButton submitting={false}/>);
button.simulate('click');
expect(goToNext).toHaveBeenCalled();
})
The problem is, reasons unknown, the test does not persist. Giving me a
Expected mock function to have been called.
EDIT: Same result would occur when attempting to mock it this way (mind the additional preventDefault()
it('should call goToNextReport on button click', () => {
const mock = jest.fn();
Service.goToNext = mock.bind(Service);
button.simulate('click', { preventDefault() {} });
expect(mock).toHaveBeenCalled();
})
I'm fairly new to JEST and js testing. It seems the mock was done properly, yet still I can't get the method to be checked.
Any ideas on the subject?
All help would be amazing!
EDIT: The issue was also submitted to the JEST github channel: https://github.com/facebook/jest/issues/8695
Try this:
jest.mock('Services/service', () => ({
goToNext: jest.fn()
}));