Skip to content
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

Merged
merged 25 commits into from Mar 14, 2019

Conversation

sebelga
Copy link
Contributor

@sebelga sebelga commented Feb 28, 2019

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.

screen shot 2019-02-28 at 17 55 07

@cjcenizal cjcenizal added v8.0.0 Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more v7.2.0 Feature:Rollups labels Feb 28, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/es-ui

@elasticmachine
Copy link
Contributor

💔 Build Failed

Copy link
Contributor

@cjcenizal cjcenizal left a 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' }),
Copy link
Contributor

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.

Copy link
Contributor Author

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) => {
Copy link
Contributor

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) => {
Copy link
Contributor

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++;
  }
};

Copy link
Contributor Author

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 () => {
Copy link
Contributor

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?

Copy link
Contributor Author

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', () => {
Copy link
Contributor

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?

Copy link
Contributor Author

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', () => {
Copy link
Contributor

@cjcenizal cjcenizal Mar 1, 2019

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@cjcenizal cjcenizal Mar 5, 2019

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. 😄

Copy link
Contributor Author

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', () => {
Copy link
Contributor

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 () => {
Copy link
Contributor

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?

Copy link
Contributor Author

@sebelga sebelga Mar 5, 2019

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', () => {
Copy link
Contributor

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());
Copy link
Contributor

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);

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@sebelga
Copy link
Contributor Author

sebelga commented Mar 5, 2019

Thanks for the review @cjcenizal ! I will try to break the spec in multiple files to make it easier to follow. 👍

@sebelga
Copy link
Contributor Author

sebelga commented Mar 5, 2019

@cjcenizal Just updated the PR, can u have another look? Cheers!

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

Copy link
Contributor

@cjcenizal cjcenizal left a 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();
}
};
Copy link
Contributor

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?

Copy link
Contributor Author

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 😊

@elasticmachine
Copy link
Contributor

💔 Build Failed

@sebelga
Copy link
Contributor Author

sebelga commented Mar 7, 2019

retest

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@bmcconaghy
Copy link
Contributor

Looks like you have an issue:

17:53:37     Cannot find module 'test_utils/enzyme_helpers' from 'testbed.js'
17:53:37 
17:53:37     However, Jest was able to find:
17:53:37     	'./testbed.js'
17:53:37 
17:53:37     You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'json', 'ts', 'tsx'].

Copy link
Contributor

@bmcconaghy bmcconaghy left a 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.

@sebelga
Copy link
Contributor Author

sebelga commented Mar 8, 2019

retest

@sebelga
Copy link
Contributor Author

sebelga commented Mar 8, 2019

Thanks for the review @bmcconaghy ! I'm actually fighting with the CI and import resolves... I know that eventually I will win 😊

@elasticmachine
Copy link
Contributor

💔 Build Failed

@sebelga
Copy link
Contributor Author

sebelga commented Mar 11, 2019

retest

@elasticmachine
Copy link
Contributor

💔 Build Failed

@tylersmalley
Copy link
Contributor

These are failing for me locally with the same errors:

node scripts/jest_integration --ci --no-cache --verbose

Should these tests not be in integration tests?

@sebelga
Copy link
Contributor Author

sebelga commented Mar 13, 2019

@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:
node x-pack/scripts/jest.js --testPathPattern <file-path>

The last failing CI complains about the plugins/rollup/__jest__/integration_tests/job_create_review.test.js test file (error: Could not locate module ui/management (...)).

Locally this test file executes without issue:

 PASS  plugins/rollup/__jest__/integration_tests/job_create_review.test.js (15.073s)
  Create Rollup Job, step 5: Metrics
    layout
      ✓ should have the horizontal step active on "Review" (576ms)
      ✓ should have the title set to "Review" (365ms)
      ✓ should have the "next" and "save" button visible (289ms)
      ✓ should go to the "Metrics" step when clicking the back button (297ms)
    tabs
      ✓ should have a "Summary" & "JSON" tabs to review the Job (298ms)
      ✓ should have a "Summary", "Terms" & "JSON" tab if a term aggregation was added (666ms)
      ✓ should have a "Summary", "Histogram" & "JSON" tab if a histogram field was added (531ms)
      ✓ should have a "Summary", "Metrics" & "JSON" tab if a histogram field was added (513ms)
    save()
      ✓ should call the "create" Api server endpoint (359ms)

Test Suites: 1 passed, 1 total
Tests:       9 passed, 9 total
Snapshots:   0 total
Time:        18.051s
Ran all test suites matching /\/Users\/sebastien\/workspace\/elastic\/kibana\/x-pack\/plugins\/rollup\/__jest__\/integration_tests\/job_create_review.test.js/i.
Waiting for the debugger to disconnect...
[1]    97281 killed     env  /Users/sebastien/.nvm/versions/node/v10.15.2/bin/node --inspect-brk=2074

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 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';
Copy link
Contributor Author

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 😊

@sebelga sebelga merged commit bc51461 into elastic:master Mar 14, 2019
@sebelga sebelga deleted the test/rollup_job_create_integration branch March 14, 2019 10:42
sebelga added a commit to sebelga/kibana that referenced this pull request Mar 14, 2019
@sebelga sebelga added the non-issue Indicates to automation that a pull request should not appear in the release notes label Mar 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Rollups non-issue Indicates to automation that a pull request should not appear in the release notes Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more v7.2.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants