How do you use js files as fixtures? any examples on this? I tried this in user-details.js:
data = {
email: function () {
const currentTimestamp = new Date().getTime();
return `test${currentTimestamp}@test`
},
firstName: 'Max',
lastName: 'Mustermann',
street: 'Some Street',
}
Then in my spec file I do this:
beforeEach(function () {
cy.fixture(env + '/user-details').as('userDetails');
});
And in the it
block of the test in the same spec file:
const userDetails = this.userDetails.data;
actions.insertStreet(userDetails.street);
But it says it cannot read property 'street' of undefined. Any ideas how to do it properly? :sweat_smile:
How do you use js files as fixtures? any examples on this? I tried this in user-details.js:
data = {
email: function () {
const currentTimestamp = new Date().getTime();
return `test${currentTimestamp}@test.`
},
firstName: 'Max',
lastName: 'Mustermann',
street: 'Some Street',
}
Then in my spec file I do this:
beforeEach(function () {
cy.fixture(env + '/user-details').as('userDetails');
});
And in the it
block of the test in the same spec file:
const userDetails = this.userDetails.data;
actions.insertStreet(userDetails.street);
But it says it cannot read property 'street' of undefined. Any ideas how to do it properly? :sweat_smile:
Share Improve this question asked Jul 1, 2021 at 23:59 mismasmismas 1,3165 gold badges33 silver badges65 bronze badges2 Answers
Reset to default 6I had the same question, and couldn't quite square the accepted answer with what I saw happening (in Cypress 9.2.1).
It turns out that when a .js
file is given as a fixture, cypress eval()
s the content of the file (actually, it uses eval-with-parentheses), and uses whatever object this results in.
So it is possible to have a file user-details.js
, which looks like
[{name: "Bob", age: 30 + 5}]
or even execute some "real" code:
["Bob", "Rob", "Job"].map((name, index) => ({user: name, age: 20 + index}))
or
(() => {
const users = ....
return users
})()
It should be noted that it's not as flexible as you might want (e.g. you cannot import code, do asynchronous things, etc), but it's more than nothing.
The question is, should you....
Quite honestly, I think this is a great way to allow making your fixtures slightly more human readable, being able to add ments, and easy conversions. And you're not bothered by JSON's ma-stictness E.g
[
// have a single user, because blah blah
{name: "Bob", date_of_birth: new Date(1979, 5, 3).valueOf() / 1000},
]
(Date(1979, 5, 3)
is much better human-readable than some unix timestamp (even though it means 5th of June, not 5th of May....))
I fully think that the .js
fixture is a good way to do this (and would be even happier if it would just import the fixture as a module and use the default export as item or something, but eval
is acceptable....), as long as the thing you're producing is constant! If you want something dynamic (or, probably, something constant that takes a bit more plexity to make), you're much better of with @seth-lutske 's suggestion (meaning that in your case, the accepted answer is what you need, however I wanted to document how .js
fixtures work in case someone else came by).
Having said all this, it's questionable why cypress uses an eval()
here, even though the suggested way is to use window.Function()
; in addition, I wonder if you want to run the risk that down the line, some other developer (which includes future-you) decides to build a system that downloads your fixtures from some external website, which means that you created a nice security hole....
It's very informative to read the github issue on Javascript fixtures (I wish I had before I dove into all this myself); if you're looking for a good workaround, see this and this ment.
As a concrete answer to the question asked, the problem is that
eval(`data = {
email: function () {
const currentTimestamp = new Date().getTime();
return \`test\${currentTimestamp}@test.\`
},
firstName: 'Max',
lastName: 'Mustermann',
street: 'Some Street',
} `)
will return exactly that object, which should be accessible on this.userDetails
. Therefore this.userDetails.data
points to the data-field in this object, which is undefined
.
The following would work:
user-details.js:
{
data: {
email: function () {
const currentTimestamp = new Date().getTime();
return `test${currentTimestamp}@test.`
},
firstName: 'Max',
lastName: 'Mustermann',
street: 'Some Street',
}
}
(or, in case you want email
to be a string rather than a function:
{
data: {
email: (function () {
const currentTimestamp = new Date().getTime();
return `test${currentTimestamp}@test.`
})(),
firstName: 'Max',
lastName: 'Mustermann',
street: 'Some Street',
}
}
)
From the cypress docs:
Load a fixed set of data located in a file.
What you are trying to load is not a fixed set of data. It is an object that contains a dynamic function that returns data that will be unique every time the function is run. You are trying to apply a json-like attitude towards something that is definitely not json. You need to think of a fixture as something that a real api call would return - a JSON, an image, binary, etc. An api call would likely not return a raw text/javascript file like your user-details.js file and expect the front end to know what to do with it.
Your options:
- Make it a true JSON and use a hard-coded value for the date
- Do an
export data = { ... }
, then import that value into your .spec.js file and use it - Just include the values inline, or create a function at the top of the .spec file that creates them for you
Sorry if that's not what you were hoping to hear
Edit: as per request, here's an example of option 3:
// any.spec.js
function createUserDetails(){
return {
email: `test${new Date().getTime()}@test.`,
firstName: 'Max',
lastName: 'Mustermann',
street: 'Some Street',
}
};
describe('your test block', => {
it('tests some stuff', () => {
const userDetails = createUserDetails();
actions.insertStreet(userDetails.street);
});
it('tests some other stuff', () => {
const userDetails = createUserDetails();
actions.insertEmail(userDetails.email);
});
});
So you define your data creator in a single place, but can reuse it in may places.