-
Notifications
You must be signed in to change notification settings - Fork 991
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
Test Scenarios #1465
Test Scenarios #1465
Conversation
…s second parameter to test function
@peterp After a couple of extensive coding sessions with @mojombo we made some updates! Trying to override This leaves out the typescript stuff though. The idea was that when declaring your scenario objects in import { scenario } from '@redwoodjs/testing'
export const standard = scenario({
comment: {
rob: {
name: 'Rob'
// ...
}
}
}) |
@cannikin This is look great, I think I can dynamically create those prisma model types at run-time. So we would default to the Could you include a comment of how to use the new function? |
Yup! The The two ways to call the method:
// users.fixtures.js
export const standard = {
users: {
rob: {
name: 'Rob Cameron'
}
}
}
export const withProfiles = {
users: {
rob: {
name: 'Rob Cameron',
profile: {
create: {
username: 'cannikin'
}
}
}
}
}
// users.test.js
import { users } from './users'
// standard scenario
scenario('returns a list of users', async (fixtures) => {
const usersList = await users()
expect(usersList[0].name).toEqual(fixtures.users.rob.name)
})
// named scenario
scenario('withProfiles', 'returns a list of users and their profiles', (fixtures) => {
const usersList = await users({ includeProfiles => true })
expect(usersList[0].profile.username).toEqual(fixtures.users.rob.profile.username)
}) |
@peterp I wasn't sure how to construct the type in https://github.com/redwoodjs/redwood/pull/1465/files#diff-dc00f5226046500fcbb89faee6cb2093feffa41a14bd9e644064267b4dd1818d since the second parameter could be a function OR a string, and if it's a string then the third parameter must be present, and be a function. TYPESCRIPT |
Maybe this code should go into a separate JS file somewhere, instead of everything being crammed into |
@cannikin Don't know if it applies here, but generally, what you're asking would be solved like this function foo(first: string, second: () => void): void;
function foo(first: string, second: string, third: () => void): void {
// implementation
} |
@peterp I updated the eslint-config stuff to add the |
Converted back to draft! Talked with @mojombo and we're going to drop the "fixtures" nomenclature and just stick with "scenarios." Jumping back and forth was getting confusing and I found myself making an aside block in the Tutorial II to try and explain the difference myself and having trouble. So the files will become |
@cannikin Let me know when you want me to try add some types to this! |
@peterp You should be able to go ahead? I'm working on the generator right now so that it creates the import { scenario } from '@redwoodjs/scenario'
export const standard = scenario({
post: {
first: {
title: 'First Post',
body: 'Lorem ipsum dolar sit amet',
},
},
}) I think? |
Holy cow @peterp I got Storybook to not blow up with I don't understand why only the parts exported from Here's a console log of Here's the same output from the dev server: |
Oh boy @peterp you're either going to love or hate this one. ;) After several hours of working with @mojombo, @Tobbe, @dthyresson and @clairefro we were able to mock the currentUser in a story! mockCurrentUser({
email: '[email protected]',
roles: ['moderator'],
}) Just insert that at the top of a story and you're good to go. How we got that working...it's either genius or insanity. We wrapped the existing mock providers in an We mock the currentUser to be There was some weirdness with declaring a global that wasn't a I can walk you through the other stuff we did, let me know! I know you said you were looking into mocking currentUser and it looked pretty easy, so maybe your way was way more elegant than this, but it was a fun exercise. :) |
@@ -35,5 +35,8 @@ export const StorybookProvider: React.FunctionComponent<{ | |||
return null | |||
} | |||
|
|||
// default to a non-existent user at the beginning of each story | |||
mockCurrentUser(null) |
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 usually like to be explicit for things like this.
mockCurrentUser(null) | |
window.mockCurrentUser(null) |
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.
Just so that people reading the code know that this is a global.
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.
Hmm, problem. Putting window.
or global.
in front of that function throws an error at build time:
@redwoodjs/core: src/storybook/StorybookProvider.tsx(39,10): error TS2339: Property 'mockCurrentUser' does not exist on type 'Window & typeof globalThis'.
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'm assuming you're offline for the day and we wanna get 0.22 out, so I'll keep this as just mockCurrentUser(null)
for now and we can revisit!
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 left a few comments about making the code easier to debug for the user, but it looks good to me!
This reverts commit 424ca60.
…h-tokens * 'main' of github.com:redwoodjs/redwood: Move whatwg-fetch from devDep to dep Adds mockCurrentUser() to api-side jest v0.22.1 v0.22.0 Revert "Configure ApolloProvider to always use fresh tokens (redwoodjs#1609)" (redwoodjs#1611) Configure ApolloProvider to always use fresh tokens (redwoodjs#1609) Ignore *.scenarios.* files. (redwoodjs#1607) upgrade prisma v2.12.1 (redwoodjs#1604) Test Scenarios (redwoodjs#1465) Use relative path to config stories location (redwoodjs#1509)
After a good couple of hours working with @peterp and @mojombo we think we came up with a pretty clean way to integrate scenarios (formerly "fixtures") into the test workflow! This PR has a bunch of backstory and reads like a thriller so grab a warm drink and snuggle up with a blanket!
Definitions
The Problem
Services are generally going to be accessing your database to retrieve/create/update/delete data. In most cases you'll want some data in the database at the start of the test. You could insert this data yourself:
8 of the 10 lines in this test (80%) is just putting data into the database and cleaning up afterwords! And if you have another test that wants to use this data you have to write all the setup again. Also, the data in the
posts
variable is just the plain object that I created, not the actual data in the database which will include additional fields likeid
orcreatedAt
which are set to default values in the database itself.So the next logical step is to just put the data insertion into a
beforeEach()
and the cleanup into anafterEach()
and actually return the data as it appears in the database:At least the test (in the
it()
function) is now dealing with just the test data, which is an improvement. But I'd still have to write thesebeforeEach
andafterEach
functions for each services test and their content is essentially the same:The next logical step is to have Redwood do the insertion and cleanup for me. That's where this PR comes in.
The Solution
Here's what we came up with. What if the test suite, the entire thing, could look like this:
Where the
scenario
argument is the sample data you inserted into the database. Let Redwood worry about seeding it and removing it afterwords. Where does the actual scenario data live? Just like the mocks solution we came up with for stories, it lives in its own file, in this casecomments.scenarios.ts
:The nested structure contains:
comment
- model namerob
- name of the fixturecomment
modelYou can insert data into nested models by using either the prisma
create
syntax (as in the example above) or declaring another model at the first level of the data structure:When you get your
scenario
object in your test, it's an object with the same structure as what you created above, but after it's been inserted in the database, so it includes data in the fields that the database itself sets, likeid
andcreatedAt
:To get this working, we create our own test function that:
We do this in our custom function named
scenario()
:Just like with the component mocks, we default to a scenario named
standard
. As long as you export that in your scenarios file, we'll use it by default. If you want to use another named scenario, export it from your*.scenarios.js
file and pass that name, as a string, as the first argument toscenario()
:This lets us preserve the existing
it()
andtest()
functions and make it clear to users that if you want to use fixtures, use our new function instead.Little history: the naming comes from a Ruby gem that @mojombo wrote years ago for Rails called fixture-scenarios.
Todo
scenario
...there were a couple changes made to some index.d.ts, global.d.ts and eslint-config files with Peter's help but it didn't fix the issue. He was going to look into that some more.