Testing react-dropzone with cypress, `isDragActive` behaves differently from real usage - cypress

I'm trying to test an application using react-dropzone. I'm using something like the following code:
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop, noClick: true})
return (
<div
{...getRootProps()}
aria-label='file-upload-dropzone'
>
<input
{...getInputProps()}
/>
<div>
{(isDragActive) ?
<span>Drop the files here...</span> :
<span>Drag and drop the files here</span>
}
</div>
</div>
)
The problem here is that:
When trying to test with the following cypress test:
cy.get('[aria-label=file-upload-dropzone]')
.selectFile(
{
contents: Cypress.Buffer.from('file contents'),
fileName: 'file.e37',
mimeType: 'text/plain',
lastModified: Date.now(),
},
{action: 'drag-drop'},
)
// the test fails here
cy.contains('div', /Drag and drop the files here/).should('exist')
the UI is stuck with "Drop the files here...". The reason seems to be that isDragActive never goes back to false, after cy.get(...).selectFile(...) has been called.
This is different from when I test the exact same code from the browser - there, isDragActive is false AND "Drag and drop the files here" is displayed when I'm finished dragging the file.
Is there any way I can get the cypress test and a real user test to behave the same for this scenario?

Since you're using a hook, try adding cy.wait(0) or setTimeout(() =>{},0) before the failing line.
This would allow the hook to complete any background action - presuming the problem is that Cypress is hogging the thread
Triggering "change" or "drop"
The following seems to be a working sequence of events.
cy.get('[aria-label=file-upload-dropzone]')
.selectFile(
{
contents: Cypress.Buffer.from('file contents'),
fileName: 'file.e37',
mimeType: 'text/plain',
lastModified: Date.now(),
},
{action: 'drag-drop'},
)
cy.contains('div', /Drop the files here.../) // passes
cy.get('[aria-label=file-upload-dropzone]')
.find('input').trigger('change', {force:true}) // without this trigger
// the next line fails
cy.contains('div', /Drag and drop the files here/) // passes
I added a simple onDrop()
const onDrop = useCallback(acceptedFiles => {
console.log('acceptedFiles', acceptedFiles)
}, [])
and from this can see two console logs - the first has the file in acceptedFiles, the second has an empty array in acceptedFiles.

Related

Cypress - Unable to run multiple tests

I am very new to cypress automation and have been following though some examples and have ran into an issue that does not appear to be addressed in any video I have seen, where multiple tests in the same 'describe' do not run as expected.
If I create the following code & run it, it all works perfectly:-
describe('My First Test', () => {
it('Open Google', () => {
cy.visit('https://google.co.uk')
cy.get('#L2AGLb > .QS5gu').click()
cy.get('.gLFyf').type('Automation Step by Step{Enter}')
})
})
I have then attempted to split up the test into individual tests, as follows:-
describe('My First Test', () => {
it('Open Google', () => {
cy.visit('https://google.co.uk')
})
it('Confirm warning', () => {
cy.get('#L2AGLb > .QS5gu').click()
cy.get('.gLFyf').type('Automation Step by Step{Enter}')
})
it('Confirm warning', () => {
cy.get('.gLFyf').type('Automation Step by Step{Enter}')
})
})
The problem now is that after opening Chrome and then going into the next test, which should allow me to type the text, a 'default blank page' is displayed and the tests then fail.
What am I missing here to be able to run these three tests fully?
Code in VS Code
Error after opening Chrome & attempting to type in box
As above really, I was hoping to be able to run all three simple tests together.
EDIT - I rolled back my version of Cypress to 10.10.0 and it works perfectly, so no idea what has changed on the latest version released yesterday.
Try it with Test Isolation turned to false.
Best Practice: Tests should always be able to be run independently from one another and still pass
This was added in Cypress 12.0.0.
But if you want to play without it,
Test Isolation Disabled
testIsolation
beforeEach test
true
- clears page by visiting about:blank
- clears cookies in all domains
- local storage in all domains
- session storage in all domains
false
does not alter the current browser context
cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
testIsolation: false,
},
})
You should visit your page for every test. You can put the cy.visit in a beforeEach function.

In my Cypress.io tests why do I need to treat a cy.task like it is asyncronous when its not

I have Cypress.io tests that use a simple Cypress task to log some table information to the terminal. For example I have a test like so:
it("Writes some console logs and checks a val", () => {
cy.task("rowLog", { id: 1, name: "foo", type: "bar" });
let one = 1;
expect(one).to.equal(2);
});
And the task, "rowLog" like so:
module.exports = (on, config) => {
on("task", {
rowLog(data) {
// use node.js console.table to pretty print table data:
console.table(data);
return null;
},
}
But the result of rowLog will not display in my terminal if I run Cypress headlessly via Cypress run. This is because the test will fail. If we switch the test so that it passes, then it will show.
However I just realized that if I treat rowLog like it's async like below. It will print the results to the terminal:
it("Writes some console logs and checks a val", () => {
// add a .then to task:
cy.task("rowLog", { id: 1, name: "foo", type: "bar" }).then(() => {
let one = 1;
expect(one).to.equal(2);
});
});
This is not what I would expect from the docs. They say that:
cy.task() yields the value returned or resolved by the task event in the pluginsFile.
(source)
And that a task can yield either a promise or a value.
I'm new to Cypress here-- is there something I'm missing or doing wrong? I'd like to be able to not have to chain my tasks with .then statements if it is just synchronous stuff like writing output to ensure everything is emitted to my terminal.
If you look into the type definition of cy.task command, you will see that it returns a Chainable (that is a promise-like entity). So it behaves like any other cy command (ansynchrounously).
As for the yield either a promise or a **value** - this statement refers to the handler of the task, not the task itself. As for the other command, Cypress will wrap a returned value into a promise if it was not done by the handler.

How to run the same test with multiple logins, URL's, and body elements in cypress.io

I have a simple test I want to create in cypress that would require a test where using a settings file I would create 1 test that executes for each entry in the settings file. The file would contain user/pwd/url/elementID and be used to login for each user at a custom URL, and validate that a specific elementID is displayed, logout, and do it again - iterating through the settings file until each is tested.
I want to do something like:
forEach(URL,uname,pwd,elementID) do
cy.request(URL)
cy.get('input:uname').btn.click
cy.get('input:pwd').btn.click
cy.get(data-cy=elementID).should(be present)
cy.get(btn.logout).btn.click
I highly doubt the above code is correct - but hopefully you get the idea. Main goal is to create a simple and quick script that will quickly iterate through an array to smoke test the functionality.
You can still iterate over your test data and create a test case out of each:
[
{
url,
uname,
pwd,
elementID,
}
].forEach(testData => {
it(`Test ${testData.uname} on ${testData.url}`, () => {
// your test code
});
});
Of course the array:
[
{
url,
uname,
pwd,
elementID,
}
]
does not need to be there in the same file, you can have it somewhere separate and import it into your spec file.
Caveat: You can only visit URLs from the same origin in one test! This code will only work if all URLs you want to test are from the same origin (i.e. same
Save your data in json format and put them in Cypress folder "fixtures"
[
{"user":"username1","pwd":"pwuser1","url":"url1","elementID":"#element_name1"},
{"user":"username2","pwd":"pwuser2","url":"url2","elementID":"#element_name2"}
]
(Don't forget the # in front of the element_name id)
Then this is your smoke_test.spec.js
//fetch the parameters from the file and save them as constant "login"
const login_data = require('../fixtures/login_data.json')
//Now you can fetch the parameters using "login_data"
describe('smoke test', () => {
it('loop through login list', () => {
//we call each entry "param" and loop through the lines of the json file
cy.get(login_data).each((param) => {
cy.visit(param.url)
cy.get('#id_of_username_field').type(param.user)
cy.get('#id_of_pw_field').type(param.pwd)
//The next line is only if you have a login button
cy.get('#id_of_login_button').click()
cy.get(param.elementID).should('be.visible')
cy.get('#id_of_logout_button)
})
})
})

Directory path is incorrect when running from cypress test runner

When I login from normal browser the login is successful with the URL : http://neelesh.zapto.org:8084/EnrolMe/indHome.html
But when I run the script from Cypress the directory location is not appended and the new URL after login is formed as : http://neelesh.zapto.org:8084/__/indHome.html
I have tried setting cypress.json with
{
"chromeWebSecurity": false,
"modifyObstructiveCode" : false
}
I have tried on chrome/electron(head and headless).
Below is my code snippet:
describe('My First Test Suite', function() {
it('My First test case', function() {
cy.visit("http://neelesh.zapto.org:8084/EnrolMe")
cy.get("#login").click()
cy.get("input[value='Individual']").click()
cy.get("#username").type('1234567890')
cy.get("#pwd").type('0646')
Cypress.Cookies.debug(true)
cy.clearCookies()
cy.get("#login").click()
cy.wait(6000)
})
})
When I run the script from Cypress the directory location is not appended and the new URL after login is formed as : http://neelesh.zapto.org:8084/__/indHome.html
It should be redirected as : http://neelesh.zapto.org:8084/EnrolMe/indHome.html
Can anyone help me on this?
This sounds like an issue with "Frame Busting". There's a related discussion for Cypress GitHub Issue #992 which may lend some help.
Your application code may contain problematic frame busting code like the following:
if (window.top !== window.self) {
window.top.location.href = window.self.location.href;
}
You can get around this by changing your application code's reference to window.self from the Application Window to the Cypress Test Runner window (window.top).
Cypress emits a series of events as it runs in your browser. You can use the emitted window:before:load application event to ensure it's done before you attempt to login.
// cypress/support/index.js
Cypress.on('window:before:load', (win) => {
Object.defineProperty(win, 'self', {
get: () => {
return window.top
}
})
})

Unit testing: getText is not a function

I'm new to unit testing and I'm trying to select an ID to select its text and check if it equals 'Inbox'. I'm getting a
TypeError in "User visits Quick Add Task starts with a blank task" elem.getText is not a function
and I don't understand why.
Test source code here.
describe('User visits Quick Add Task', () => {
it('starts with a blank task', () => {
browser.url('http://localhost:8080/');
const elem = $('#inbox-clickable');
console.log(elem.getText());
assert.equal(elem.getText(), 'Inbox');
})
});
For me the solution to a very similar problem was adding the missing #wdio/sync package to my package.json.
I already had previously added sync: true, to my wdio.conf.js and remove all async and await from my testing code.

Resources