I build simple e2e test with Nightwatch. This simple e2e test is passed successfully.
Next, I build second test to use Chai.js / Mocha.js. But this test raises exception.
// First(successfully passed)
module.exports = {
'increment counter': browser => {
browser
.url(process.env.VUE_DEV_SERVER_URL)
.click("button#plus-btn")
.assert.containsText('h1', '1')
.end()
},
"decrement counter": browser => {
browser
.url(process.env.VUE_DEV_SERVER_URL)
.click("button#minus-btn")
.assert.containsText('h1', '-1')
.end()
}
}
// Second
describe('counter testing', () => {
describe('press plus button', () => {
it('increments counter', (browser) => {
browser
.url(process.env.VUE_DEV_SERVER_URL)
.click("button#plus-btn")
.expect.element('h1').text.to.equal('1')
.end()
})
})
describe('press minus button', () => {
it('decrements counter', (browser) => {
browser
.url(process.env.VUE_DEV_SERVER_URL)
.click("button#minus-btn")
.expect.element('h1').text.to.equal('-1')
.end()
})
})
})
Exception message
TypeError: browser.url(...).click(...).expect.element(...).text.to.equal(...).end is not a function
What should I do next?
As per the docs, you should call end in a different way when using expect for assertions. I think it's because the expect way is based on chai. Changing your tests to look like this will work
it("increments counter", browser => {
browser
.url(process.env.VUE_DEV_SERVER_URL)
.click("button#plus-btn")
.expect.element("h1")
.text.to.equal("1");
// See how end is called differently
browser.end();
});
Related
I am learning Cypress along with JavaScript. I am running into a problem that I am not certain how to search it into documentation. The site I started testing has the typical wait issues so I encountered a very good solution here.
Now my test is looking in this way
/// <reference types="Cypress" />
let appHasStarted
function spyOnAddEventListener (win) {
// win = window object in our application
const addListener = win.EventTarget.prototype.addEventListener
win.EventTarget.prototype.addEventListener = function (name) {
if (name === 'change') {
// web app added an event listener to the input box -
// that means the web application has started
appHasStarted = true
// restore the original event listener
win.EventTarget.prototype.addEventListener = addListener
}
return addListener.apply(this, arguments)
}
}
function waitForAppStart() {
// keeps rechecking "appHasStarted" variable
return new Cypress.Promise((resolve, reject) => {
const isReady = () => {
if (appHasStarted) {
return resolve()
}
setTimeout(isReady, 0)
}
isReady()
})
}
describe('Main test suite', () => {
beforeEach(() => {
cy.visit('http://mercadolibre.com.ar',{
onBeforeLoad: spyOnAddEventListener
}).then({ timeout: 10000 }, waitForAppStart)
})
it('search first scanner', () => {
cy.contains('nav-search-input').type("scanner bluetooth para auto")
})
})
The problem with this is, I should replicate spyOnAddEventListener, waitForAppStart and variable appHasStarted at the beginning of every source file but I want to avoid this. How could properly extend this functions as a part of the internal source project without replicating in every test source? I have tried to make a simple source JavaScript file at the root of the project but when I import it, Cypress clients give an unrelated plug error like this one:
It looks like you've added the code to /cypress/plugins/index.js, but that is for task extensions (code that requires NodeJS access).
The two functions can be added to a file, ideally in the /cypress/support folder
wait-for-app-utils.js
let appHasStarted
function spyOnAddEventListener (win) {
...
}
function waitForAppStart() {
...
}
module.exports = {
spyOnAddEventListener,
waitForAppStart
}
test
import {spyOnAddEventListener, waitForAppStart} from '../support/wait-for-app-utils.js'
describe('Main test suite', () => {
beforeEach(() => {
cy.visit('http://mercadolibre.com.ar', {
onBeforeLoad: spyOnAddEventListener
}).then({ timeout: 10000 }, waitForAppStart)
})
Another approach is to wrap it all up (including the visit) into a custom command. Now there's no need to export and import, the command will be available globally.
/cypress/support/commands.js
let appHasStarted
function spyOnAddEventListener (win) {
...
}
function waitForAppStart() {
...
}
Cypress.Commands.add('visitAndWait', (url) =>
cy.visit(url, { onBeforeLoad: spyOnAddEventListener })
.then({ timeout: 10000 }, waitForAppStart)
)
test
describe('Main test suite', () => {
beforeEach(() => {
cy.visitAndWait('http://mercadolibre.com.ar')
})
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?
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.
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 working with my jasmine test suite, and I'm getting a weird error that causes the test to fail. The code works, and I've verified by debugging the tests that the values I expect are the values present, but the test fails. I get this as output:
Failed
at stack (/Users/username/code/source/client/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:2200:17)
at buildExpectationResult (/Users/username/code/source/client/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:2170:14)
at Spec.expectationResultFactory (/Users/username/code/source/client/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:777:18)
at Spec.addExpectationResult (/Users/username/code/source/client/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:450:34)
at Env.fail (/Users/username/code/source/client/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1192:25)
at next.fail (/Users/username/code/source/client/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4046:19)
This is what my tests look like
describe('the returned type', () => {
afterEach(store.clearActions);
beforeEach(done => {
mock.onPatch(url).replyOnce(200, { updated_at: moment().toISOString() });
save(4, value)(store.dispatch, store.getState)
.then(() => setTimeout(done, 750));
});
it('should have correct type', () => {
const action = store.getActions()[0];
expect(action.type).toEqual(LAST_SAVED_UPDATE);
});
});