-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MB-12189 set up fe fake data generator and factory tools #9142
MB-12189 set up fe fake data generator and factory tools #9142
Conversation
We import it in a few places and with the eslint config I'm going to commit next, we need to have it in our dependencies
Set up stricter checks for extraneous deps so that we won't just allow any extraneous deps, but still allow dev dependencies where needed. Also allow param-reassignment so that factories can more easily work with objects in the postBuild step.
…nerator-and-factory-tools
The `zipCodeByState` function uses ranges of numbers for each state to generate the zip, but since some state zip codes start with 0's, they get stripped out. This adds the zero back to the front if needed.
@@ -69,7 +83,6 @@ module.exports = { | |||
{ | |||
files: ['*.stories.js', '*.stories.jsx', 'setupTests.js'], | |||
rules: { | |||
'import/no-extraneous-dependencies': 'off', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pulled this out of here and put it above so that we only allow dev dependencies but still check for other extraneous dependencies.
const fake = (callback) => { | ||
return perBuild(() => callback(faker)); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is partially syntactic sugar. It makes it so than in a factory, instead of having to write:
...
fields: {
streetAddress1: perBuild(() => faker.address.streetAddress())
}
...
you can instead write:
...
fields: {
streetAddress1: fake((f) => f.address.streetAddress()),
}
...
which, tbh, imo isn't a huge win for this alone.
But, one other advantage is that it lets us set the locale once and then everything that uses fake
will automatically get it. In version 3.0.0 of faker
, there will be per localization packages, which will make this a little less necessary since we could just make sure we always import from the same package everywhere. Though it might still be beneficial to have it here so that we don't have to be ensuring we import from the same place and just leave that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems worth it
src/utils/test/factories/address.js
Outdated
export const BLANK_ADDRESS = 'blank'; | ||
export const ADDRESS_WITHOUT_COUNTRY = 'omitCountry'; | ||
|
||
const addressFactory = build({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Will engineers still be able to add specific values for certain fields? For instance with pricing wanting to choose zipcodes that are a certain distance from each other
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think we should be able to do that. If you want to just pass some override values you can do that by doing something like:
const fakeAddress = addressFactory({ overrides: { streetAddress1: '1600 Washington Ave' } });
For something like the postalCode that is set as part of the postBuild
, I don't think there is a way to pass in a value and set the value. Though we can just create a value using the factory and then do a manual override by doing something like:
const fakeAddress = addressFactory({ overrides: { streetAddress1: '1600 Washington Ave' } });
fakeAddress.postalCode = '90210';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we think it's worth turning the override keys into constants? I'm working on the ServiceWorker factory, and the keys down in the weight_allotment
can be tricky to get correct in an override. Constantizing them would at least make them available to Intellisense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scott and I met earlier and I learned from him that in order for an override to work, it needs to be defined as one of the base fields. So if I add postalCode
to the field definitions, we can pass in an override. The postBuild
can still overwrite it, but I can add a conditional to leave it as is if it's already been set. I can push up a commit with those changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also talked about having constants for the fields. I agree it would be good to do that because intellisense is nice and the point of these factories is to speed up test writing. We talked about a few options for how that could work. I'm going to push up one version, but plan on talking more about it during our PR review time on Tuesday, and bringing things up at the BE/FE checkin.
const fake = (callback) => { | ||
return perBuild(() => callback(faker)); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems worth it
Request: can a link to this PR be added to the ADR for this concept? |
src/utils/test/factories/address.js
Outdated
}, | ||
}); | ||
|
||
export default addressFactory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the rationale behind having this be a default import over a named import?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that was more because I started with only one export and our eslint config requires it to be default. Then I added more things, but by then I wasn't sure if it was being used in other branches so I didn't want to cause merge issues. I'm happy to change it though and just communicate out on the channel in case anyone has a branch and is using this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Giving a 👍 for the eslintrc change
Yeah, great idea! I can open up a PR for the ADR. |
…nerator-and-factory-tools
@pearl-truss here's the other PR: transcom/mymove-docs#205 |
…nerator-and-factory-tools
return perBuild(() => callback(faker)); | ||
}; | ||
|
||
const loadSpec = (fileName) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got the idea for this from swagger-api/swagger-js#1410
Came up while I was looking for a way to use the swagger client with local files. I didn't want to point to a URL since the swagger files could change with a PR and then we'd need to update the path for those. An alternative is linked to in that thread to spin up a local server just to serve the file, but that seems like more work than is needed. Especially because once the yaml is parsed and converted to json, we don't really need the swagger client, we can access all of our definitions through the json this outputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I tried the codegen tool a bit running:
docker run --rm -v ${PWD}:/local swaggerapi/swagger-codegen-cli generate \
-i local/swagger/internal.yaml \
-l javascript \
-o /local/src/utils/test/swagger-client/internal \
-DmodelDocs=false -DmodelTests=false -DapiDocs=false -DapiTests=false
It then needs to be installed with:
yarn add --dev file:./src/utils/test/swagger-client/internal
This seems to give us access to the definitions. One nice thing about this is that it gives us code completion since there are actual variables for properties like the state enum, rather than just working with json. I haven’t gotten a chance to mess with it too much though.
src/utils/test/factories/address.js
Outdated
[ADDRESS_FIELDS.COUNTRY]: 'US', // Likely change once we support more than just CONUS moves. | ||
}, | ||
traits: { | ||
[ADDRESS_TRAITS.BLANK]: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really a factory question, is there a reason we would have an address with blank fields? I see that streetAddress2 gets saved as a blank string in the customer app. Or is there some null/undefined behavior where this is useful?
It looks like we have to define all of the fields on the model and then make a trait if we ever want to remove fields down to say what is only required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that came up because when I initially started using this in the residential address tests, there had been an empty address. Though after looking at the internal yaml, it looked like the address as a whole was nullable, and could be omitted when empty, so I ended up not using this. I should probably just remove the trait since there isn't a current use case for it.
To your point about having to define all the fields and then removing them, that is what needs to be done because if you don't define a field, you can't override it at build-time, which is a bit annoying. Thanks for reminding me, I'll add that to the things that would make it easier to work with factories
}; | ||
|
||
export const addressFactory = build({ | ||
fields: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should id
be included as an option here to represent an already created record?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah I need to add that one and eTag
. I can push that up today.
|
||
return address; | ||
}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be a good pattern to start where there is a 'required' trait that doesn't automatically fill in all of the optional fields. This may not be perfect across forms but usually we are worried about validations in our tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, that would probably be a good thing to have, I can add that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like a good start to me. I am curious how it will work with nested objects but this was a good first demonstration case.
…nerator-and-factory-tools
Update `NO_COUNTRY` trait to be `ONLY_BASIC_ADDRESS` because the new fields also need to be removed for cases when country was removed, at least in the tests it's being used in currently.
const loadSpec = (fileName) => { | ||
const yamlPath = path.join(process.cwd(), `./swagger/${fileName}`); | ||
|
||
// RA Summary: eslint - security/detect-non-literal-fs-filename - OWASP Path Traversal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job so far! I was just learning about factories in React so it was really interesting seeing how they are implemented!
Closing in favor of #9233 |
Jira ticket for this change
Summary
This PR sets up our FE to be able to have factories to more easily generate test data.
Document with some findings and thoughts: 🔒 https://dp3.atlassian.net/wiki/spaces/MT/pages/1895071898/Test+factories+and+fake+data+generators
Main changes
postBuild
step.en_US
as the locale for generated datafake
function that will be used by factories to generate fake data.postBuild
function,traits
, andtraits
withpostBuild
.Other changes
eslint
config changes I made. We already have the package because of other dependencies, but since we import it in some files, it's better to list it as an explicit dependency.Testing the changes
💻
Running Tests
yarn test src/pages/MyMove/Profile/ResidentialAddress.test.jsx
Verification Steps for Author
These are to be checked by the author.