-
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] Improve simulator. Add more click-through tests and panel tests. #74601
Changes from all commits
900d945
8fb44fc
ef8b8b1
58b4a22
1ccea95
4c13ab7
d8800d9
9e5cef0
5a77179
8245e1e
4763965
998a664
47f0cff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,6 @@ import { mount, ReactWrapper } from 'enzyme'; | |
import { createMemoryHistory, History as HistoryPackageHistoryInterface } from 'history'; | ||
import { CoreStart } from '../../../../../../../src/core/public'; | ||
import { coreMock } from '../../../../../../../src/core/public/mocks'; | ||
import { connectEnzymeWrapperAndStore } from '../connect_enzyme_wrapper_and_store'; | ||
import { spyMiddlewareFactory } from '../spy_middleware_factory'; | ||
import { resolverMiddlewareFactory } from '../../store/middleware'; | ||
import { resolverReducer } from '../../store/reducer'; | ||
|
@@ -48,6 +47,7 @@ export class Simulator { | |
dataAccessLayer, | ||
resolverComponentInstanceID, | ||
databaseDocumentID, | ||
history, | ||
}: { | ||
/** | ||
* A (mock) data access layer that will be used to create the Resolver store. | ||
|
@@ -61,6 +61,7 @@ export class Simulator { | |
* a databaseDocumentID to pass to Resolver. Resolver will use this in requests to the mock data layer. | ||
*/ | ||
databaseDocumentID?: string; | ||
history?: HistoryPackageHistoryInterface<never>; | ||
}) { | ||
this.resolverComponentInstanceID = resolverComponentInstanceID; | ||
// create the spy middleware (for debugging tests) | ||
|
@@ -79,8 +80,9 @@ export class Simulator { | |
// Create a redux store w/ the top level Resolver reducer and the enhancer that includes the Resolver middleware and the `spyMiddleware` | ||
this.store = createStore(resolverReducer, middlewareEnhancer); | ||
|
||
// Create a fake 'history' instance that Resolver will use to read and write query string values | ||
this.history = createMemoryHistory(); | ||
// If needed, create a fake 'history' instance. | ||
// Resolver will use to read and write query string values. | ||
this.history = history ?? createMemoryHistory(); | ||
|
||
// Used for `KibanaContextProvider` | ||
const coreStart: CoreStart = coreMock.createStart(); | ||
|
@@ -95,9 +97,6 @@ export class Simulator { | |
databaseDocumentID={databaseDocumentID} | ||
/> | ||
); | ||
|
||
// Update the enzyme wrapper after each state transition | ||
connectEnzymeWrapperAndStore(this.store, this.wrapper); | ||
} | ||
|
||
/** | ||
|
@@ -112,6 +111,16 @@ export class Simulator { | |
return this.spyMiddleware.debugActions(); | ||
} | ||
|
||
/** | ||
* EUI uses a component called `AutoSizer` that won't render its children unless it has sufficient size. | ||
* This forces any `AutoSizer` instances to have a large size. | ||
*/ | ||
private forceAutoSizerOpen() { | ||
this.wrapper | ||
.find('AutoSizer') | ||
.forEach((wrapper) => wrapper.setState({ width: 10000, height: 10000 })); | ||
} | ||
|
||
/** | ||
* Yield the result of `mapper` over and over, once per event-loop cycle. | ||
* After 10 times, quit. | ||
|
@@ -124,6 +133,7 @@ export class Simulator { | |
yield mapper(); | ||
await new Promise((resolve) => { | ||
setTimeout(() => { | ||
this.forceAutoSizerOpen(); | ||
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 was thinking you'd just call this when you need it in another public method (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. why? 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. Few thoughts on this:
With the stuff above in mind:
All that being said, putting this in 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. Map is at the center of this 'new approach' to testing. Adding something that only has to be there for some edge-case-set of tests seems like it would
The general idea is that it's a necessary but ugly thing to do, so you should avoid doing it when you can - and injecting it into our Enzyme I guess at the very least you should add an additional comment inside the .map so the reader can understand when/why that happens. 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. lets save this discussion into a ticket or in-code docs and defer deciding until later. |
||
this.wrapper.update(); | ||
resolve(); | ||
}, 0); | ||
|
@@ -174,6 +184,13 @@ export class Simulator { | |
); | ||
} | ||
|
||
/** | ||
* The items in the submenu that is opened by expanding a node in the map. | ||
*/ | ||
public processNodeSubmenuItems(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:map:node-submenu-item"]'); | ||
} | ||
|
||
/** | ||
* Return the selected node query string values. | ||
*/ | ||
|
@@ -206,38 +223,38 @@ export class Simulator { | |
} | ||
|
||
/** | ||
* An element with a list of all nodes. | ||
* The titles of the links that select a node in the node list view. | ||
*/ | ||
public nodeListElement(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-list"]'); | ||
public nodeListNodeLinkText(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-list:node-link:title"]'); | ||
} | ||
|
||
/** | ||
* Return the items in the node list (the default panel view.) | ||
* The icons in the links that select a node in the node list view. | ||
*/ | ||
public nodeListItems(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-list:item"]'); | ||
public nodeListNodeLinkIcons(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-list:node-link:icon"]'); | ||
} | ||
|
||
/** | ||
* The element containing the details for the selected node. | ||
* Link rendered in the breadcrumbs of the node detail view. Takes the user to the node list. | ||
*/ | ||
public nodeDetailElement(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-detail"]'); | ||
public nodeDetailBreadcrumbNodeListLink(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-detail:breadcrumbs:node-list-link"]'); | ||
} | ||
|
||
/** | ||
* The details of the selected node are shown in a description list. This returns the title elements of the description list. | ||
* The title element for the node detail view. | ||
*/ | ||
private nodeDetailEntryTitle(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-detail:entry-title"]'); | ||
public nodeDetailViewTitle(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-detail:title"]'); | ||
} | ||
|
||
/** | ||
* The details of the selected node are shown in a description list. This returns the description elements of the description list. | ||
* The icon element for the node detail title. | ||
*/ | ||
private nodeDetailEntryDescription(): ReactWrapper { | ||
return this.domNodes('[data-test-subj="resolver:node-detail:entry-description"]'); | ||
public nodeDetailViewTitleIcon(): ReactWrapper { | ||
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. Just making a note here about following the page objects 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. What do you mean exactly? |
||
return this.domNodes('[data-test-subj="resolver:node-detail:title-icon"]'); | ||
} | ||
|
||
/** | ||
|
@@ -253,8 +270,14 @@ export class Simulator { | |
* The titles and descriptions (as text) from the node detail panel. | ||
*/ | ||
public nodeDetailDescriptionListEntries(): Array<[string, string]> { | ||
const titles = this.nodeDetailEntryTitle(); | ||
const descriptions = this.nodeDetailEntryDescription(); | ||
/** | ||
* The details of the selected node are shown in a description list. This returns the title elements of the description list. | ||
*/ | ||
const titles = this.domNodes('[data-test-subj="resolver:node-detail:entry-title"]'); | ||
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-detail makes me think the information is physically tied with the node in some way in the view rather than in the panel, maybe 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. actually, ignore me. |
||
/** | ||
* The details of the selected node are shown in a description list. This returns the description elements of the description list. | ||
*/ | ||
const descriptions = this.domNodes('[data-test-subj="resolver:node-detail:entry-description"]'); | ||
const entries: Array<[string, string]> = []; | ||
for (let index = 0; index < Math.min(titles.length, descriptions.length); index++) { | ||
const title = titles.at(index).text(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
interface Options { | ||
/** | ||
* The entity_id of the selected node. | ||
*/ | ||
selectedEntityID?: string; | ||
} | ||
|
||
/** | ||
* Calculate the expected URL search based on options. | ||
*/ | ||
export function urlSearch(resolverComponentInstanceID: string, options?: Options): string { | ||
if (!options) { | ||
return ''; | ||
} | ||
const params = new URLSearchParams(); | ||
if (options.selectedEntityID !== undefined) { | ||
params.set(`resolver-${resolverComponentInstanceID}-id`, options.selectedEntityID); | ||
} | ||
return params.toString(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,8 +71,9 @@ describe('Resolver, when analyzing a tree that has no ancestors and 2 children', | |
}); | ||
}); | ||
|
||
it(`should show the node list`, async () => { | ||
await expect(simulator.map(() => simulator.nodeListElement().length)).toYieldEqualTo(1); | ||
it(`should show links to the 3 nodes (with icons) in the node list.`, async () => { | ||
await expect(simulator.map(() => simulator.nodeListNodeLinkText().length)).toYieldEqualTo(3); | ||
await expect(simulator.map(() => simulator.nodeListNodeLinkIcons().length)).toYieldEqualTo(3); | ||
}); | ||
|
||
describe("when the second child node's first button has been clicked", () => { | ||
|
@@ -152,5 +153,20 @@ describe('Resolver, when analyzing a tree that has two related events for the or | |
relatedEventButtons: 1, | ||
}); | ||
}); | ||
describe('when the related events button is clicked', () => { | ||
beforeEach(async () => { | ||
const button = await simulator.resolveWrapper(() => | ||
simulator.processNodeRelatedEventButton(entityIDs.origin) | ||
); | ||
if (button) { | ||
button.simulate('click'); | ||
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. ❔ Mentioned above, but I had this as a public method on the simulator, like |
||
} | ||
}); | ||
it('should open the submenu', async () => { | ||
await expect( | ||
simulator.map(() => simulator.processNodeSubmenuItems().map((node) => node.text())) | ||
).toYieldEqualTo(['2 registry']); | ||
}); | ||
}); | ||
}); | ||
}); |
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.
🔴 Doc comment on export.
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 catch