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

[Security Solution][Resolver] Graph Control Tests and Update Simulator Selectors #74680

Merged
merged 9 commits into from
Aug 13, 2020

Conversation

michaelolo24
Copy link
Contributor

Summary

Adds tests for the graph panning and zoom controls

Checklist

@michaelolo24 michaelolo24 added v8.0.0 release_note:skip Skip the PR/issue when compiling release notes Team:Endpoint Data Visibility Team managing the endpoint resolver Feature:Resolver Security Solution Resolver feature v7.10.0 labels Aug 10, 2020
@michaelolo24 michaelolo24 marked this pull request as ready for review August 10, 2020 16:46
@michaelolo24 michaelolo24 requested review from a team as code owners August 10, 2020 16:46
@elasticmachine
Copy link
Contributor

Pinging @elastic/endpoint-app-team (Feature:Resolver)

@elasticmachine
Copy link
Contributor

Pinging @elastic/endpoint-data-visibility-team (Team:Endpoint Data Visibility)

Copy link
Contributor

@oatkiller oatkiller 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 the PR. tests look good. put in a few suggestions

* This manually runs the animation frames tied to a configurable timestamp in the future
*/
public runAnimationFramesTimeFromNow(time: number = 0) {
this.sideEffectSimulator.controls.time = new Date().getTime() + time;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.sideEffectSimulator.controls.time = new Date().getTime() + time;
this.sideEffectSimulator.controls.time = time;

new Date isn't needed 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.

thx, done

/**
* Simulator which allows you to explicitly simulate resize events and trigger animation frames
*/
public readonly sideEffectSimulator: SideEffectSimulator;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public readonly sideEffectSimulator: SideEffectSimulator;
private readonly sideEffectSimulator: SideEffectSimulator;

This could be private

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

const originNode = simulator.processNodeElements({ entityID: originEntityID });
expect(originNode.getDOMNode()).toHaveStyle(originalPositionStyle);

const westPanButton = simulator.graphControlElement().find('[data-test-subj="west-button"]');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you mind changing this data-test-subj to use the same format as the others?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

});

it('should load graph controls', () => {
expect(simulator.graphControlElement().length).toBe(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test might not be needed. The existence of a wrapper element isn't important to the AC of any feature and its specific to the implementation details. Maybe check that the 4 buttons exist 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.

Yea, I checked for the existence of all the functional buttons instead

expect(originNode.getDOMNode()).toHaveStyle({ left: '796.93132px', top: '535.5792px' });
});

it('should pan south', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you phrase the spec like:

describe('when the user clicks the south panning button', () => {
  beforeEach(/* the click code does here*/)
  it('should show the origin node lower on the screen', () => {
    // the expect here

This way we can easily add more it blocks that have the same setup and we can easily tell which code sets up the condition vs which code asserts stuff

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, this works really well. Thanks for the tip 👍

it('should display all cardinal panning buttons and the center button', () => {
const westPanButton = simulator
.graphControlElement()
.find('[data-test-subj="resolver:graph-controls:west-button"]');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.find will return react components as well as DOM nodes. In order to test the DOM only you need to use domNodes: https://github.com/elastic/kibana/blob/master/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx#L246

Would you consider using that instead?

Also, these tests would be more resilient to change if you use .map. As it is, these pass because Resolver shows the controls immediately on the first render. If we (for example) made the controls show up after data had loaded then these tests would likely fail. They might even fail now if you make the mock data access layer resolve the API calls in the next event-loop cycle.

For that reason, would you consider using .map?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or consider resolveWrapper (uses map indirectly)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I forgot about that concern when I wrote this tbh. Updated and refactored to use .map and domNodes. Thanks!

@michaelolo24
Copy link
Contributor Author

@elasticmachine merge upstream

@elasticmachine
Copy link
Contributor

merge conflict between base and head

});

it('should show the origin node further left on the screen', async () => {
expect(originNode.getDOMNode()).toHaveStyle({ left: '796.93132px', top: '535.5792px' });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine to merge but long term I have some concerns:

This test relies on react to keep the same DOM node for the origin after the user clicks the pan element. If that component got a new DOM node for some reason then originNode would point to an old element that may not be in the DOM any more.

Also this relies on the react code to update the style of the element in the same event loop cycle when the user clicks the panning button.

I think we should use the pattern of calling expect on an async iterable. How about I make those changes after you merge?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michaelolo24 Here's a commit with my ideas: 3de7be9

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, cherry-picked it in and updated the selectors based on our zoom meeting

@oatkiller oatkiller mentioned this pull request Aug 12, 2020
7 tasks
@michaelolo24 michaelolo24 changed the title [Security Solution][Resolver] Graph control tests [Security Solution][Resolver] Graph Control Tests and Update Simulator Selectors Aug 12, 2020
@michaelolo24
Copy link
Contributor Author

@elasticmachine merge upstream

@michaelolo24
Copy link
Contributor Author

@elasticmachine merge upstream

* @param entityID The entity ID of the proocess node to select in
* @param selector The selector for the child element of the process node
*/
public processNodeChildElements(entityID: string, selector: string): ReactWrapper {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bkimmel, thoughts? I wanted to generalize your method a bit for any other buttons or things that might be added to the process node in the future

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❔ It looks good, generally. Two points:

  1. The parameter isn't really a selector (it only feeds the data-test-subj attrib). So I'd say adjust the comment.
  2. To make that even more clear, you could put a "ByDataTestSubj" at the end of the method name (but that's awful long)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, we can talk about these patterns later today as well. Gonna merge this for now

await expect(
simulator.map(() => simulator.testSubject('resolver:node-list:node-link:title').length)
).toYieldEqualTo(3);
await expect(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems this is duplicated

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see. the old version had an expect for 'text' and an expect for 'icons'

@@ -147,7 +151,10 @@ describe('Resolver, when analyzing a tree that has two related events for the or
it('should render a related events button', async () => {
await expect(
simulator.map(() => ({
relatedEventButtons: simulator.processNodeRelatedEventButton(entityIDs.origin).length,
relatedEventButtons: simulator.processNodeChildElements(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the purpose of the special treatment 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.

Same as below. Retain the original intention here. I could move all of the actual selector string into this test or maybe even export the processNodeElementSelector and build the string at this level. Alternatively, I could tie the actual selector with the associated entity-id (see below comment)

@@ -156,33 +163,35 @@ describe('Resolver, when analyzing a tree that has two related events for the or
describe('when the related events button is clicked', () => {
beforeEach(async () => {
const button = await simulator.resolveWrapper(() =>
simulator.processNodeRelatedEventButton(entityIDs.origin)
simulator.processNodeChildElements(entityIDs.origin, 'resolver:submenu:button')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just testSubject here? This couples the test to DOM structure. Is there a reason?

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 was just doing a one to one replacement that generalized this test a bit more. The only way to really not have this tied to the structure of the DOM would be to replace the resolver:submenu:button with a reference to the entity-id, maybe resolver:node-${entityID}:submenu:button. Is that what you're suggesting?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see what you mean. I wasn't necessarily suggesting anything but here's my thoughts:

  • The user doesn't care about the DOM structure and so if we don't rely on it our tests will be less brittle.
  • It would be cool if we are only coupled to data-test attributes. That should make it simple to keep tests working. If we remove a wrapper div, I would hope that no tests break.

Maybe at our next office hours we can discuss those ideas more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is some semantic weight to. e.g. the button being a child / ("owned by" in the case of semantics) of the process node. I say keep it as is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or to put it another way: If the relationship between the node and the interactive element it's expected to contain changes in some way, I think it's good that a test picks that up.

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Build metrics

async chunks size

id value diff baseline
securitySolution 7.3MB +235.0B 7.3MB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream


it('should show the origin node as larger on the screen', async () => {
await expect(originNodeStyle()).toYieldObjectEqualTo({
width: '427.7538290724795px',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ this comes from the yarn snapshot deal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

>
<EuiPanel className="panning-controls" paddingSize="none" hasShadow>
<div className="panning-controls-top">
<button
className="north-button"
data-test-subj="north-button"
data-test-subj="resolver:graph-controls:north-button"
title="North"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ later to fix these to better things like "Pan the scene up" or something. North is not a good description for what this does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, we can talk about this during office hours :)

@michaelolo24 michaelolo24 merged commit c34e30e into elastic:master Aug 13, 2020
@michaelolo24 michaelolo24 deleted the graph-control-tests branch August 13, 2020 18:55
michaelolo24 added a commit to michaelolo24/kibana that referenced this pull request Aug 13, 2020
…r Selectors (elastic#74680)

Co-authored-by: oatkiller <robert.austin@elastic.co>
# Conflicts:
#	x-pack/plugins/security_solution/public/resolver/view/clickthrough.test.tsx
gmmorris added a commit to gmmorris/kibana that referenced this pull request Aug 14, 2020
* master: (23 commits)
  Adding API test for custom link transaction example (elastic#74238)
  [Uptime] Singular alert (elastic#74659)
  Revert "attempt excluding a codeowners directory" (elastic#75023)
  [Metrics UI] Remove TSVB dependency from Metrics Explorer APIs (elastic#74804)
  Remove degraded state from ES status service (elastic#75007)
  [Reporting/Functional] unskip pagination test (elastic#74973)
  [Resolver] Stale query string values are removed when resolver's component instance ID changes. (elastic#74979)
  Add public url to Workplace Search plugin (elastic#74991)
  [Reporting] Update more Server Types for TaskManager (elastic#74915)
  [I18n] verify select icu-message options are in english (elastic#74963)
  Make data.search.aggs available on the server. (elastic#74472)
  [Security Solution][Resolver] Graph Control Tests and Update Simulator Selectors (elastic#74680)
  attempt excluding a codeowners directory
  [ML] DF Analytics: allow failed job to be stopped by force via the UI (elastic#74710)
  Add kibana-core-ui-designers team (elastic#74970)
  [Metrics UI] Fix inventory footer misalignment (elastic#74707)
  Remove legacy optimizer (elastic#73154)
  Update design-specific GH code-owners (elastic#74877)
  skip test Reporting paginates content elastic#74922
  [Metrics UI] Add Jest tests for alert previews (elastic#74890)
  ...
michaelolo24 added a commit that referenced this pull request Aug 14, 2020
…mulator Selectors (#74680) (#75008)

Co-authored-by: oatkiller <robert.austin@elastic.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Resolver Security Solution Resolver feature release_note:skip Skip the PR/issue when compiling release notes Team:Endpoint Data Visibility Team managing the endpoint resolver v7.10.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants