I am writing tests for Dark Mode Actions in cypress and I am operating mostly on header. Because of it I am catching it very often using cy.get("header). I am wondering if there is any way to save it in any variable so there is no need to catch it every time and use something like header.contains for example. Documentation of cypress says that simple const header = cy.get("header") doesn't work. Do you know any method to solve this problem so my code will be a little bit cleaner?
Part of test code
it("toggles darkmode", () => {
//when
cy.visit("localhost:3000");
cy.get("header").contains("title", "moon-icon").click({ force: true });
cy.get("header").should("contain", "sun-icon");
cy.get("header").contains("title", "sun-icon").click({ force: true });
cy.get("header").should("contain", "moon-icon");
});
it("remebers dark mode after refresh", () => {
//when
cy.visit("localhost:3000");
cy.get("header").contains("title", "moon-icon").click({ force: true });
cy.reload();
//then
cy.get("header").should("contain", "sun-icon");
});
Assuming all of your tests in this same describe block have the same setup, you could alias the cy.get('header') in a beforeEach.
describe('test', () => {
beforeEach(() => {
cy.visit('localhost:3000');
cy.get('header').as('header');
});
it("toggles darkmode", () => {
//when
cy.get("#header").contains("title", "moon-icon").click({ force: true });
cy.get("#header").should("contain", "sun-icon");
cy.get("#header").contains("title", "sun-icon").click({ force: true });
cy.get("#header").should("contain", "moon-icon");
});
});
Take a look at .within() to set scope of commands.
cy.get("header").within($header => {
cy.contains("title", "moon-icon")
.click()
.should("contain", "sun-icon")
cy.contains("title", "sun-icon")
.click()
.should("contain", "moon-icon")
})
Related
Wanted to know the difference b/w .tobeCalled and .tobeCalled().
it('should emit edit as true when confirmation methods is called with true', () =>{
component.confirmation(true)
expect(component.confirmation).toHaveBeenCalled
component.confirmationStatus.subscribe((res: {edit: boolean}) => {
expect(res).toEqual({ edit: true})
})
})
when i use the above block my code is getting covered in coverage.code coverage image for for above code block
when i use the below code block its not getting coverage in the coverage. wanted to understand the difference b/w those code block.
it('should emit edit as true when confirmation methods is called with true', () =>{
const spy = spyOn(component, 'confirmation')
component.confirmation(true)
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledWith(true)
component.confirmationStatus.subscribe((res: {edit: boolean}) => {
expect(res).toEqual({ edit: true})
})
})
code coverage image for above code block
Thanks in advance.
I have a test that requires the value of isBanana to be false.
The test works (and isBanana is false) when I mock the function in the top level index.test.js. However this breaks other tests (as they require isBanana to be true).
jest.mock("myapp-api-functions", () => {
console.log(`executing mock function`);
return {
...jest.requireActual("myapp-api-functions"),
isBanana: false,
};
});
If I move the jest.mock() into the body of the test, isBanana is true and the test doesn't work.
it(`should error when someone tries to use the mock account in production`, async () => {
jest.mock("myapp-api-functions", () => {
console.log(`executing mock function`);
return {
...jest.requireActual("myapp-api-functions"),
isBanana: false,
};
});
...same test function that previously passed...
});
The mock doesn't work and the test fails.
How can I mock the primitive value for a single test?
Calls to jest.mock get hoisted to the top of the code block.
To avoid this behaviour you can instead use jest.doMock e.g.
it(`should error when someone tries to use the mock account in
production`, async () => {
jest.doMock("myapp-api-functions", () => {
console.log(`executing mock function`);
return {
...jest.requireActual("myapp-api-functions"),
isBanana: false,
};
});
// Same test function that previously passed...
});
This will allow you to specify mock behaviour for a specific test.
I'm writing tests (with Jest and React Testing Library) for a form React component. I have a method that runs on form submit:
const onSubmit = (data) => {
// ...
setIsPopupActive(true);
// ...
};
and useEffect that runs after isPopupActive change, so also on submit:
useEffect(() => {
if (isPopupActive) {
setTimeout(() => {
setIsPopupActive(false);
}, 3000);
}
}, [isPopupActive]);
In the test, I want to check, whether the popup disappears after 3 seconds. So here's my test:
it('Closes popup after 3 seconds', async () => {
const nameInput = screen.getByPlaceholderText('Imię');
const emailInput = screen.getByPlaceholderText('Email');
const messageInput = screen.getByPlaceholderText('Wiadomość');
const submitButton = screen.getByText('Wyślij');
jest.useFakeTimers();
fireEvent.change(nameInput, { target: { value: 'Test name' } });
fireEvent.change(emailInput, { target: { value: 'test#test.com' } });
fireEvent.change(messageInput, { target: { value: 'Test message' } });
fireEvent.click(submitButton);
const popup = await waitFor(() =>
screen.getByText(/Wiadomość została wysłana/)
);
await waitFor(() => {
expect(popup).not.toBeInTheDocument(); // this passes
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 3000);
});
});
However, I'm getting the error:
expect(received).toHaveBeenCalledTimes(expected)
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function setTimeout]
What am I doing wrong?
Jest 27 has breaking changes for fakeTimers. It seems Jest contributors doesn't update documentation on time. This comment on Github issues confirms it. Moreover, here related PR.
Well, you can solve your problem by two ways.
Configure Jest to use legacy fake timers. In jest.config.js you can add line (but it not works for me):
module.exports = {
// many of lines omited
timers: 'legacy'
};
Configure legacy fake timers for individually test suite, or even test:
jest.useFakeTimers('legacy');
describe('My awesome logic', () => {
// blah blah blah
});
It's preferably to use new syntax based on #sinonjs/fake-timers. But I can't find working example for Jest, so I'll update this answer as soon as possible.
The below approach worked
beforeEach(() => {
jest.spyOn(global, 'setTimeout');
});
afterEach(() => {
global.setTimeout.mockRestore();
});
it('Test if SetTimeout is been called', {
global.setTimeout.mockImplementation((callback) => callback());
expect(global.setTimeout).toBeCalledWith(expect.any(Function), 7500);
})
In your case setTimeout is not a mock or spy, rather, it's a real function. To make it a spy, use const timeoutSpy = jest.spyOn(window, 'setTimeout'). And use timeoutSpy in the assertion.
You could also test not the fact of calling the setTimeout function, but assert that setIsPopupActive was called once, and with false. For this you might need to do jest.runOnlyPendingTimers() or jest.runAllTimers()
I saw some other posts about local storage but they all relate to tokens and login.
We have an iframe that gets created and pops in from the right on our site upon first visit, I'm trying to keep this iframe from ever opening. A dev put an identifier in place for me to tell it's my Cypress test and to not fire the iframe but it's flaky.
I am using the plugin https://www.npmjs.com/package/cypress-localstorage-commands to handle my local storage.
This is in my Command file:
import "cypress-localstorage-commands";
In my test, I have the following:
beforeEach(() => {
cy.restoreLocalStorage();
cy.setLocalStorage('is_cypress_test', 'true');
})
afterEach(() => {
cy.saveLocalStorage();
})
However, this frequently fails and the iframe opens. When it works, it also prints out to console that Cypress was detected (this is something added on our sites code to verify it was working).
Here is the basic look of my test.
/// <reference types="Cypress" />
describe(`it browses to xxxx`, () => {
// sets up service cookie to preserve session
Cypress.Cookies.defaults({
preserve: 'foo',
});
beforeEach(() => {
cy.setLocalStorage('is_cypress_test', 'true');
cy.restoreLocalStorage();
})
afterEach(() => {
cy.saveLocalStorage();
})
it(`should log in via a POST, and browse xxx`, () => {
cy.serviceLoginByCSRF(Cypress.env('user_name'), Cypress.env('password'));
cy.visit('/#/asitepage');
});
describe(`it checks all xxxxx`, () => {
it(`should verify xxxxxx`, () => {
cy.get('h3').should('be.visible').invoke('text').then(data => {
let regex = /\n|\*|Back/g;
cy.textCleanup(data, regex).should('eq', 'bar');
});
});
});
describe(`it checks all yyyy`, () => {
it(`should verify yyyy`, () => {
cy.get('h3').should('be.visible').invoke('text').then(data => {
let regex = /\n|\*|Back/g;
cy.textCleanup(data, regex).should('eq', 'foo');
});
});
});
});
Beamer code
<!-- Beamer for product updates -->
<script>
var beamer_config = {
product_id: "foobar",
selector: "#beamer",
user_email: 'example#test.blogspot.gov',
user_firstname: 'Hank',
user_lastname: 'Williams',
filter: 'production',
onopen: function(){
// localStorage.setItem("is_cypress_test", "true") -- is test
if(localStorage.getItem("is_cypress_test")){
console.log("Skipping beamer load for Cypress");
return false;
}
}
};
</script>
<script type="text/javascript" src="https://asite.js" defer="defer"></script>
<!-- // Beamer for product updates -->
I'm wondering if I'm setting this in the wrong way, or wrong area?
Any help, or notes on how best to use this so it will always have that in localStorage before every test would be greatly appreciated.
Thoughts?
Thanks
Had the same issue, what fixed it for me was adding the following code to commands.js:
Cypress.LocalStorage.clear = function (keys, ls, rs) {
return;
}
I am trying to spy on $.ajax in Jasmine 2.0 tests. Here is a simplified example (TypeScript) showing my scenario:
describe("first test", () => {
var deferred = jQuery.Deferred();
spyOn($, "ajax").and.callFake((uri: string, settings: JQueryAjaxSettings) => {
return deferred.resolve("ThisIsADummyResult");
});
it("should return dummy result", done => {
$.ajax("http://somedummyserver.net").then(result => {
expect(result).toBe("ThisIsADummyResult");
done();
});
});
});
describe("second test", () => {
var deferred = jQuery.Deferred();
spyOn($, "ajax").and.callFake((uri: string, settings: JQueryAjaxSettings) => {
return deferred.resolve("ThisIsAnotherResult");
});
it("should return another result", done => {
$.ajax("http://somedummyserver.net").then(result => {
expect(result).toBe("ThisIsAnotherResult");
done();
});
});
});
firstTest as well as second test work if I run them alone. However, if I run both tests as shown above, I get the following error message: ajax has already been spied upon.
So my questions are:
Shouldn't the spies be reset by Jasmine after each test automatically? Why doesn't that work in my case?
Is there another way of using spyOn which makes Jasmine reset the spies?
How can I manually reset the spies?
Update: I continued experimenting and found a possible solution myself. If I set up the spies inside of the it spec, both tests run fine. Here is the code for first test showing what I mean:
describe("first test", () => {
it("should return dummy result", done => {
var deferred = jQuery.Deferred();
spyOn($, "ajax").and.callFake((uri: string, settings: JQueryAjaxSettings) => {
return deferred.resolve("ThisIsADummyResult");
});
$.ajax("http://somedummyserver.net").then(result => {
expect(result).toBe("ThisIsADummyResult");
done();
});
});
});
Still, it would be very interesting why the first version does not work. Why does Jasmine not reset the spies in the first version whereas it does in the second one?
For stuff that is used across all tests but you need it reset for each test use 'beforeEach' : http://jasmine.github.io/2.0/introduction.html#section-Setup_and_Teardown
Jasmine does not magically know which lines of your describe body you want reevaluated for each 'it' block.