Closed tnrich closed 5 years ago
What is this line logging? console.log('files:', files)
@jennifer-shehane given this code
cy.uploadFiles('.tg-dropzone',["filePath1", "filePath2"])
that line was logging
[fileObj, fileObj]
where the two fileObjs are identical.
Curiously, my coworker changed the above code just slightly to this:
Cypress.Commands.add('uploadFiles', (selector, fileUrlOrUrls, type = '') => {
const fileUrls = Array.isArray(fileUrlOrUrls)
? fileUrlOrUrls
: [fileUrlOrUrls]
let files = [];
Promise.all(
fileUrls.map(fileUrl => {
const nameSegments = fileUrl.split('/')
const name = nameSegments[nameSegments.length - 1]
return cy
.fixture(fileUrl, 'base64')
.then(Cypress.Blob.base64StringToBlob)
.then(blob => {
const file = new File([blob], name, { type })
files.push(file);
})
})
).then(() => {
console.log('files:', files)
const event = { dataTransfer: { files } }
return cy.get(selector).trigger('drop', event)
})
})
and it seems to be working now
Closing as resolved.
This isn't fixed, attempting to use two fixtures in a test is incredibly buggy and inconsistent. It's like JavaScript has somehow been broken by cypress here... common promise behaviour doesn't seem to apply in this strange cypress world. What did you do?
Thomas can we ask you to paste the test code snippet that is broken?
Sent from my iPhone
On Mar 18, 2019, at 19:17, Thomas notifications@github.com wrote:
This isn't fixed, attempting to use two fixtures in a test is incredibly buggy and inconsistent. It's like JavaScript has somehow been broken by cypress here... common promise behaviour doesn't seem to apply in this strange cypress world. What did you do?
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.
Cypress Version 3.2.0 (3.2.0) OSX: 10.14.3 node --version v10.11.0 Chrome Version 73.0.3683.75
it.only(`hmmmm`, () => {
const fixtures = []
Promise.all([
cy.fixture('list-guess-incorrect.json').then(fx => fixtures.push(fx)),
cy.fixture('list-head.json').then(fx => fixtures.push(fx)),
]).then(fx => {
console.log(fx)
console.log(fixtures)
})
fx
logs an array with the same item twice, the result from the first cy.fixture()
call... But, it should be [1,1]
as array.push()
will return the number of items inserted...fixtures
logs empty at the time of logging, but you can expand the array in console to see the two different array items. This is because the array is being populated long after the promises resolve. This is too late for tests to use it.Better yet.
it.only(`hmmm`, () => {
const fixtures = []
Promise.all([
cy.fixture('list-guess-incorrect.json').then(fx => fixtures.push(fx)),
cy.fixture('list-head.json').then(fx => fixtures.push(fx)),
]).then(fx => {
cy.wait(1000).then(() => {
console.log(fx)
console.log(fixtures)
})
})
Now fx
(the first log) still logs the same item twice... but the second log fixtures
now logs [undefined, item]
... \(ツ)/¯
Using Cypress.Promise.all
seems to work!
But still... Need to close over an array that I mutate instead of using the resolved values :/
const fixtures = []
Cypress.Promise.all([
cy.fixture('list-head.json').then(fx => fixtures.push(fx)),
cy.fixture('list-guess-incorrect.json').then(fx => fixtures.push(fx)),
]).then((fx) => {
console.log(fx)
console.log(fixtures)
})
fx
correctly logs [1,1]
fixtures
correctly logs the two fixturesTrying to make this more terse and trust the normal JavaScript promise behaviour
Cypress.Promise.all([
cy.fixture('list-head.json'),
cy.fixture('list-guess-incorrect.json')
]).then((fixtures) => {
console.log(fixtures)
})
@jennifer-shehane , same issue as @tomatau has. Getting the first fixture item twice when using Cypress.Promise.all
I recommend looking at the "Fixtures" recipe I have added to https://github.com/cypress-io/cypress-example-recipes - which shows how to load one or several fixtures
Sorry, what would be the recommended approach when loading 3 or more fixtures? So called pyramid of doom of fixtures
does not scale all that well.
Yeah I'm not keen on the callback hell being the recommendation here either, especially when the language has features to move past that now.
This worked for me and could scale to more.
const fixtures = []
Cypress.Promise.all([
cy.fixture('first.json').then(fx => fixtures.push(fx)),
cy.fixture('second.json').then(fx => fixtures.push(fx)),
]).then((fx) => {
const [ first, second ] = fixtures
})
edit: it should also be faster than the nesting as the requests are being made concurrently.
After a lot of head-scratching and inspiration from previous comments I finally got a command working, which takes an array of fixturepaths and names, and produces a single payload for the upload plugin. not only does it help when I'm actually testing upload of multiple files, but it should also save me some time instead of uploading each of my required test file one by one. Next step is to fetch the right MimeType for each file base on the filename extension, but I think I can easily solve it with an npm package "mime-types".
Cypress.Commands.add('uploadMultiple', ({localPaths = [], fileNames = [], mimeType = 'image/png'}) => {
const fixtures = (paths) => Promise.all(paths.map(p => cy.fixture(p)))
const encoding = 'base64'
fixtures(localPaths).then(filesContents => {
console.log(filesContents)
const payload = fileNames.map((fileName, i) => (
{
fileContent: filesContents[i],
fileName,
encoding,
mimeType
}))
console.log(payload)
cy.get('input[multiple]')
.upload(payload)
})
})```
I did something like this, thanks to @tomatau and it's working so far
--- modules/load_fixtures.js---
export const loadFixtures = arrayOfFixtures => {
const fixtures = []
const promises = arrayOfFixtures.map(fixture => cy.fixture(fixture).then(res => fixtures.push(res)))
return Cypress.Promise.all(promises).then(() => {
return fixtures
})
}
--- integration/test.spec.js---
import { loadFixtures } from 'modules/load_fixtures.js'
it('description', function() {
loadFixtures(['beneficiaries.json', 'transactions.json']).then(
([beneficiaries, transaction]) => {
console.log(beneficiaries)
console.log(transaction)
})
})
Unfortunately this solution doesn't seem to work anymore, cypress complains about mixing promises and cypress commands. Though it seems that the fact that cypress actually runs these commands sequentially allows you to do something even simpler:
const loadFixtures = (fixtures: string[]) => {
const res = [];
fixtures.map((name, i) => cy.fixture(name).then((f) => (res[i] = f))); // could also use `res.push(f)` they should be equivalent
return cy.wrap(res);
}
Because we're mutating the res
array, even though cy.wrap
will likely be called on an empty array (since the cy.fixture
calls will be enqueued but not yet run), that's not a problem because no cypress command after that will run either because they would only be enqueued. In this way any cy command functionally acts as a guard condition for any other, so we can guarantee that anything chaining off of this would see a fully populated array.
I normally would not recommend mutating state this way because it can lead to some confusing behavior but in this case it seems to be a necessary evil.
Hi there, I'm wondering how to upload an array of fixtures for them to be drag and dropped into a file upload. My code currently looks like:
I want to use it like so:
This promise all doesn't seem to work. Multiple files are returned in the promise.all, but they are all the same! Is this because you can't really use cypress like normal promises? If so, how can I do what I want -- upload an array of fixtures?
Thanks!