-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Resolver] simulator tests select elements directly instead of using descendant selectors. #75058
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -175,14 +175,24 @@ export class Simulator { | |
} | ||
|
||
/** | ||
* Return an Enzyme ReactWrapper for any child elements of a specific processNodeElement | ||
* | ||
* @param entityID The entity ID of the proocess node to select in | ||
* @param selector The selector for the child element of the process node | ||
* The button that opens a node's submenu. | ||
*/ | ||
public processNodeChildElements(entityID: string, selector: string): ReactWrapper { | ||
public processNodeSubmenuButton( | ||
/** nodeID for the related node */ entityID: string | ||
): ReactWrapper { | ||
return this.domNodes( | ||
`${processNodeElementSelector({ entityID })} [data-test-subj="${selector}"]` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❔ This is one case where the structure of the selector before ("button that is owned by the process") was valuable I think. I don't see that reflected as much in simplifying it to "a button with an ID". Someone could move that button out from the node, changing the semantics and this test would still pass. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How exactly would removing a wrapper div change the semantics? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The wrapper gets an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even though the submenu button gets tagged with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we could assert this "contains" stuff in the test itself, but then to Michael's point earlier, it kinda beggars the purpose of this separate method altogether. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's exactly what I'm going for. Why would we want that to break the test? Is there some observable behavior that's being broken? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📏 Post discussion at office hours: We do want to test things like that buttons are contained by certain elements (say with certain aria-roles or they have some other kind of equivalent aria- relationship like That allows us to keep methods on the simulator more simple/single-purpose ("just get this element") and write tests for the things we care to assert. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we discussed this. The DOM structure is important. These abstractions are still useful. With them, not every tests needs to be coupled to the DOM structure. These methods may change in the future to rely on the DOM structure, that should be fine. Also, we will have separate tests that check the DOM structure and a11y roles. |
||
`[data-test-subj="resolver:submenu:button"][data-test-resolver-node-id="${entityID}"]` | ||
); | ||
} | ||
|
||
/** | ||
* The primary button (used to select a node) which contains a label for the node as its content. | ||
*/ | ||
public processNodePrimaryButton( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Node's have 2 buttons, one to select it, one to open its submenu. This is the one that selects it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above, but if we decide to stay this route, would a name like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That name could also work. We could also drop There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. aria attributes could work. The primary reason I lean towards naming this the effect the button has is that it provides the reader an initial idea of what any related tests should be doing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see what you mean. Related idea: perhaps we could have a |
||
/** nodeID for the related node */ entityID: string | ||
): ReactWrapper { | ||
return this.domNodes( | ||
`[data-test-subj="resolver:node:primary-button"][data-test-resolver-node-id="${entityID}"]` | ||
); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,13 +62,13 @@ describe('Resolver, when analyzing a tree that has no ancestors and 2 children', | |
selectedOriginCount: simulator.selectedProcessNode(entityIDs.origin).length, | ||
unselectedFirstChildCount: simulator.unselectedProcessNode(entityIDs.firstChild).length, | ||
unselectedSecondChildCount: simulator.unselectedProcessNode(entityIDs.secondChild).length, | ||
processNodeCount: simulator.processNodeElements().length, | ||
nodePrimaryButtonCount: simulator.testSubject('resolver:node:primary-button').length, | ||
})) | ||
).toYieldEqualTo({ | ||
selectedOriginCount: 1, | ||
unselectedFirstChildCount: 1, | ||
unselectedSecondChildCount: 1, | ||
processNodeCount: 3, | ||
nodePrimaryButtonCount: 3, | ||
}); | ||
}); | ||
|
||
|
@@ -82,13 +82,14 @@ describe('Resolver, when analyzing a tree that has no ancestors and 2 children', | |
}); | ||
|
||
describe("when the second child node's first button has been clicked", () => { | ||
beforeEach(() => { | ||
// Click the first button under the second child element. | ||
simulator | ||
.processNodeElements({ entityID: entityIDs.secondChild }) | ||
.find('button') | ||
.first() | ||
.simulate('click'); | ||
beforeEach(async () => { | ||
const button = await simulator.resolveWrapper(() => | ||
simulator.processNodePrimaryButton(entityIDs.secondChild) | ||
); | ||
// Click the second child node's primary button | ||
if (button) { | ||
button.simulate('click'); | ||
} | ||
}); | ||
it('should render the second child node as selected, and the origin as not selected, and the query string should indicate that the second child is selected', async () => { | ||
await expect( | ||
|
@@ -141,23 +142,20 @@ describe('Resolver, when analyzing a tree that has two related events for the or | |
graphElements: simulator.testSubject('resolver:graph').length, | ||
graphLoadingElements: simulator.testSubject('resolver:graph:loading').length, | ||
graphErrorElements: simulator.testSubject('resolver:graph:error').length, | ||
originNode: simulator.processNodeElements({ entityID: entityIDs.origin }).length, | ||
originNodeButton: simulator.processNodePrimaryButton(entityIDs.origin).length, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this test used to check for the presence of a div, now it checks for a button (that the user can see and interact w/) |
||
})) | ||
).toYieldEqualTo({ | ||
graphElements: 1, | ||
graphLoadingElements: 0, | ||
graphErrorElements: 0, | ||
originNode: 1, | ||
originNodeButton: 1, | ||
}); | ||
}); | ||
|
||
it('should render a related events button', async () => { | ||
await expect( | ||
simulator.map(() => ({ | ||
relatedEventButtons: simulator.processNodeChildElements( | ||
entityIDs.origin, | ||
'resolver:submenu:button' | ||
).length, | ||
relatedEventButtons: simulator.processNodeSubmenuButton(entityIDs.origin).length, | ||
})) | ||
).toYieldEqualTo({ | ||
relatedEventButtons: 1, | ||
|
@@ -166,7 +164,7 @@ 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.processNodeChildElements(entityIDs.origin, 'resolver:submenu:button') | ||
simulator.processNodeSubmenuButton(entityIDs.origin) | ||
); | ||
if (button) { | ||
button.simulate('click'); | ||
|
@@ -183,7 +181,7 @@ describe('Resolver, when analyzing a tree that has two related events for the or | |
describe('and when the related events button is clicked again', () => { | ||
beforeEach(async () => { | ||
const button = await simulator.resolveWrapper(() => | ||
simulator.processNodeChildElements(entityIDs.origin, 'resolver:submenu:button') | ||
simulator.processNodeSubmenuButton(entityIDs.origin) | ||
); | ||
if (button) { | ||
button.simulate('click'); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -404,6 +404,8 @@ const UnstyledProcessEventDot = React.memo( | |
}} | ||
tabIndex={-1} | ||
title={eventModel.processNameSafeVersion(event)} | ||
data-test-subj="resolver:node:primary-button" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thoughts on this pattern? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I don't see a scenario where we add more buttons in the future, but if we do, then we can change it then. The possible future scenario I'm thinking about is something like this potentially around a process node: https://tympanus.net/codrops/2013/08/09/building-a-circular-navigation-with-css-transforms/ Probably a lot of accessibility concerns with that that I have not thought about though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like it |
||
data-test-resolver-node-id={nodeID} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sweeet |
||
> | ||
<span className="euiButton__content"> | ||
<span className="euiButton__text" data-test-subj={'euiButton__text'}> | ||
|
@@ -433,6 +435,7 @@ const UnstyledProcessEventDot = React.memo( | |
menuTitle={subMenuAssets.relatedEvents.title} | ||
projectionMatrix={projectionMatrix} | ||
optionsWithActions={relatedEventStatusOrOptions} | ||
nodeID={nodeID} | ||
/> | ||
)} | ||
</EuiFlexItem> | ||
|
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 no longer relies on the DOM structure.
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 wonder if it's better to just remove this method entirely to just use the
testSubject
and make theprocessNodeSubmenuButton
a separate utility function? Only rationale is keeping the simulator as generic as possible. so you would have something likeexpect(simulator().testSubject(processNodeSubmenuButton(entityId))).toBe(blah)
...then again that looks a bit gross 🤷♂️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 actually considered this, but the
testSubject
method takes just a single test subject string whileprocessNodeSubmenuButton
involves two data-attribute selectors.I also considered packing all of the info we need into the
data-test-subj
attribute.I wasn't 100% on either of those, but I think this PR is still an improvement.
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.
Yea, it can really go either way. We could have testSubject take an options obj that builds the selector based on what's defined a la your
processNodeElementSelector
. So testSubject would now take the followingThere 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.
yah that might work. I first want to discuss this topic: is it important that the buttons are descendants of the node's wrapper
<div />
. Having methods likeprocessNodeSubmenuButton
provides a level of abstraction that will allow us to easily change our mind on that. In this PR it selects an element using two attribute selectors. If we decide that the DOM structure does matter then we could change that to use a descendant selector.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 to be resolved by @bkimmel's comment below. Thanks