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
[Rollup] Redux integration test job creation #32223
[Rollup] Redux integration test job creation #32223
Conversation
Pinging @elastic/es-ui |
💔 Build Failed |
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.
👏 👏 👏 Wow, bravo! This is terrific, Seb. I love how this test suite basically makes functional tests unnecessary. Thanks for doing this!! I had a few suggestions that I think we should address before merging (e.g. more verbose test description, and extracting a bug fix to a separate PR so we can backport it). But for all of the other stuff, I'd be OK with addressing them in later PRs.
I had a pretty hard time reading the test file because it was so long, though. I would really like to see it split up into separate files per step. I also had a few questions about various tests which I believe would make more sense within component-specific test files -- but if you disagree please help me understand your approach better. This is something we can address in a later PR.
I also had some ideas about that page object pattern we talked about -- take a look and let me know what you think! Also something that can be addressed later.
@@ -21,8 +21,8 @@ const dayOrdinalToDayNameMap = { | |||
const monthOrdinalToMonthNameMap = { | |||
0: i18n.translate('xpack.rollupJobs.util.month.january', { defaultMessage: 'January' }), | |||
1: i18n.translate('xpack.rollupJobs.util.month.february', { defaultMessage: 'February' }), | |||
2: i18n.translate('xpack.rollupJobs.util.month.march', { defaultMessage: 'April' }), | |||
3: i18n.translate('xpack.rollupJobs.util.month.april', { defaultMessage: 'March' }), | |||
2: i18n.translate('xpack.rollupJobs.util.month.march', { defaultMessage: 'March' }), |
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 seems like a bug fix we should extract into a separate PR, and backport to 6.7.
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, I will create a separate PR for it
interval: '24h' | ||
}; | ||
|
||
const initUserActions = (component, findTestSubject) => { |
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.
For large test files like this, it might make sense to try extracting all of the helper logic into a sibling helper file, e.g. job_create.test_helpers.js
. If this is possible then it might also enable splitting this file up into multiple files.
} | ||
}; | ||
|
||
const initGoToStep = (fillFormFields, clickNextStep) => async (step) => { |
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 haven't been able to test this, but perhaps this could be made terser with a loop?
const initGoToStep = (fillFormFields, clickNextStep) => async (step) => {
const completeStep = [
async () => await fillFormFields('logistics'),
async () => await fillFormFields('date-histogram'),
];
let currentStep;
while (currentStep < targetStep) {
if (completeStep[currentStep]) {
completeStep[currentStep]();
}
clickNextStep();
currentStep++;
}
};
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.
Will try that 👍
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true); | ||
}); | ||
|
||
it('should not allow spaces', async () => { |
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.
Many of these tests seem more appropriate as unit tests specific to the validation functions in steps_config
, though maybe I'm just not understanding the testing paradigm we're using here. Can you help me understand why they should be here instead?
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 the idea is to be as close as possible to how our app (or app section in this case) will be used by a user. If we test in isolation all the files in step_configs
:
- We'll create a lot of files 😊
- We don't have any guarantee that our component uses them. So we might have a green CI but a broken app.
Here we have the best of both world (functional and unit testing) without the complexity of a full browser + API integration testing. It also allows us to refactor without worying about the implementation details.
And... if a product/QA person reads the spec files, he understands a first sight and in 1 place the validation of our form, withouth the need to go throught dozens of files and hoping things are wired correctly.
}); | ||
}); | ||
|
||
describe('rollup cron', () => { |
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.
Similarly, my instinct would be to move these tests into test file that's specific to the cron editor component, i.e. steps/components/cron_editor/cron_editor.test.js
. Could you help me understand the rationale for putting them 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.
If we were to create the cron editor as a reusable component, it might make sense. But we don't so I prefer to test it in integration.
This article helps in understanding the reasoning: https://kentcdodds.com/blog/write-tests
Integration tests strike a great balance on the trade-offs between confidence and speed/expense. This is why it's advisable to spend most (not all, mind you) of your effort there.
expect(testSubjectExists('rollupJobCreateFrequencyHourlyMinuteSelect')).toBe(true); | ||
}); | ||
|
||
it('should allow to select any minute from 00 -> 59', () => { |
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've seen Jest tests tend to use test
over it
. It might help people differentiate between a Mocha and Jest test at a glance if we use test
in our Jest 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.
I prefer to stick to the RSpec and use describe
and it
. (see comment here https://stackoverflow.com/a/54907283/5642633). Aside, if I'm not wrong, files ended in .test.js
are all Jest, no?
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, Jest test files end in .test.js
. But in terms of optimizing for readability, it reduces a bit of friction when it's easier to identify code by just looking at it than by looking at the file name. And if you look at any of the Jest docs, they all use test
. Not a blocker though, just pointing it out. 😄
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.
in terms of optimizing for readability
Funny, when I read a test (to.equal
vs toEqual
) I don't wonder what is the testing framework underneath. I do when I write it obviously. 😄
}); | ||
}); | ||
|
||
describe('when no terms are availalbe', () => { |
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.
Typo: available
await goToStepAndOpenFieldChooser(); | ||
}); | ||
|
||
it('should display the numeric & keyword fields available', async () => { |
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.
Similar to my other comments, I wonder if we could move this into tests for the specific component, i.e. components/field_list/field_list.test.js
?
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.
As I commented above, this would bring less "confidence" to the test.
addHistogramFieldToList(); | ||
}); | ||
|
||
describe('--> invalid', () => { |
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.
Could we add a more verbose description? I think we're testing all of the different ways the interval input can enter into an invalid state?
form, | ||
goToStep, | ||
getEuiStepsHorizontalActive, | ||
} = initTestBed()); |
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.
Seeing this, I feel like it really begs to be refactored into the page object model. Then instead of having findTestSubject
calls everywhere, like this:
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
The page object could expose individual parts of the UI, so the code reads a bit more like a story:
expect(rollupJobWizard.nextButton.props().disabled).toBe(true);
This would also mean testSubjectExists
is no longer necessary, since we could do this instead:
expect(rollupJobWizard.nextButton.exists()).toBe(true);
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 will have to give it some thought as it will complicate things and I'm not sure it brings that much value. For a few objects always present on the screen, it might be nice. But for elements that only appear in the DOM on a specific step (or when the detail panel opens) then it either does not work, or we have to add special methods to dynamically update the objects (the complication I'm talking about).
And then we'd have 2 lines (1 to update the objects and 1 to test) instead of one. We can talk about it over zoom if u want.
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.
Cool, I'm also willing to take a stab at it myself in a separate PR.
Thanks for the review @cjcenizal ! I will try to break the spec in multiple files to make it easier to follow. 👍 |
@cjcenizal Just updated the PR, can u have another look? Cheers! |
💔 Build Failed |
💔 Build Failed |
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.
Thanks for breaking up this file into multiple files -- I find them much easier to scan and digest now. Code LGTM, but don't forget to extract out that bugfix (#32223 (comment)).
if (step > 5) { | ||
clickNextStep(); | ||
} | ||
}; |
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.
Did the loop idea not work out?
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.
Forgot to update that part. Just did it 😊
💔 Build Failed |
retest |
💔 Build Failed |
💔 Build Failed |
Looks like you have an issue:
|
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.
LGTM, I like the organization of this, very easy to follow what is happening and the tests themselves are very terse and clear.
retest |
Thanks for the review @bmcconaghy ! I'm actually fighting with the CI and import resolves... I know that eventually I will win 😊 |
💔 Build Failed |
retest |
💔 Build Failed |
These are failing for me locally with the same errors:
Should these tests not be in integration tests? |
@tylersmalley Those tests are not the integrations tests from Kibana. I call them integration because there are not unit tests either. 😊 Those tests load a whole app section (a react router "page") and test redux integration on the full page. But no real Http requests calls are made on the Kibana server, they are all mocked. So locally these tests pass without issue with: The last failing CI complains about the Locally this test file executes without issue:
|
💔 Build Failed |
…reate_integration
💔 Build Failed |
💔 Build Failed |
💚 Build Succeeded |
import sinon from 'sinon'; | ||
|
||
import { MINUTE, HOUR, DAY, WEEK, MONTH, YEAR } from '../../public/crud_app/services'; | ||
import { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } from '../../../../../src/legacy/ui/public/index_patterns'; |
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.
@cjcenizal @njd5475 For info: this relative import from .test.js
works 😊
This PR adds a "redux" integration test for the creation of a rollup job. I was in doubt if creating multiple files for each step or one big test file. I finally sticked with the later to avoid duplication of testbed setup. I also realize it was easy to navigate the test file by folding the blocks thanks to well organized
described
sections.