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

[Spike] EUI snapshot serializer #6769

Closed
wants to merge 1 commit into from

Conversation

cee-chen
Copy link
Contributor

Summary

This PR is a 1-2 day spike at writing an exportable set of EUI snapshot serializer. The goal of the serializer is to flatten EUI components in snapshots only (i.e., not full jest.mock() scenarios that replacement the component in all test scenarios) to reduce exposure to the inner workings of our components (e.g. classes, styles, DOM). The reduced snapshotted DOM would ideally result in smaller downstream snapshot churn in Kibana, and as such fewer codeowner pings.

This PR was opened mostly to preserve a copy of the spike code as well as provide a place where certain parts of the code can be discussed - it is not meant to be merged in.

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_6769/

Copy link
Contributor Author

@cee-chen cee-chen left a comment

Choose a reason for hiding this comment

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

@JasonStoltz @tkajtoch, I'd love your thoughts on this spike and how (or if) we should even move forward with it.

Questions that may be helpful to answer:

  1. Does this code (in particular the regexes) feel as fragile to you as they do to me?
  2. Is this something we want to keep moving forward with? Is this worth the time, or would maintaining this work cost us more time than simply updating snapshots with every upgrade?

Comment on lines +9 to +11
export { euiEnzymeShallowSnapshotSerializer } from './shallow';
export { euiEnzymeMountSnapshotSerializer } from './mount';
export { euiEnzymeRenderSnapshotSerializer } from './render';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just want to illustrate a large part of the problem - there's 4 different render methods (Enzyme has 3, RTL has 1) that need to be solved for when writing these serializers, which effectively quadruples the amount of work and testing needed.

/**
* Snapshot serializer
*/
export const euiEnzymeShallowSnapshotSerializer = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shallow is the most robust of all the serializers, because... well.. it's basically already what we want 🥲 We wouldn't be in this situation if everyone in Kibana were using shallow snapshots however.

export const euiRTLRenderSnapshotSerializer = {
test: (val: any) => {
if (val instanceof HTMLElement) {
return val.className?.startsWith('eui');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TBH, I think even just taking the RTL render snapshot serializer alone is where you start to realize things are getting kinda dicey. Basically what I'm doing is regexing HTML which everyone knows is a wild path to go down.

It also feels somewhat fragile because it relies on us correctly setting className="euiSomeComponent" as a class hook/API on everything we render, which to be fair, we do do 90% of the time, but then don't for some things like nested DOM, internal components, etc.

Comment on lines +157 to +164
expect(rtlRender(pageTest).container.firstChild).toMatchInlineSnapshot(`
<EuiPageTemplate>
<main
class="emotion-euiPageInner"
id="EuiPageTemplateInner_generated-id"
>
<section
class="emotion-euiPageSection-l-top-transparent"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above comment - the reason these aren't getting properly converted to, e.g. <EuiPageInner> or <EuiPageSection> is because of class naming convention 💀

Comment on lines +261 to +271
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="-1"
/>
<EuiDataGrid />
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="-1"
/>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just want to highlight this - components with focus traps or sibling components also add another vector to account for that's difficult to predict - we're starting to get into the business of writing serializers/regexes for 3rd party library DOM 😬

So, e.g., EuiModal and EuiFlyout etc will be super difficult to get a clean snapshot for.

Comment on lines +275 to +285
expect(render(gridTest)).toMatchInlineSnapshot(`
Array [
<div
data-focus-guard="true"
style="width:1px;height:0px;padding:0;overflow:hidden;position:fixed;top:1px;left:1px"
tabindex="-1"
/>,
<div
class="euiDataGrid__focusWrap"
data-focus-lock-disabled="disabled"
>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also want to highlight this particular incredibly annoying quirk of Enzyme's/Cheerio's render - it's the only one of the render options that can return an array of nodes (not actual DOM nodes, mind you, but their pseudo node objects which can't actually be parsed as DOM/HTML...). This makes it so sibling scenarios need a for loop to parse, but also simultaneously super difficult to correctly parse individually because their API documentation is very lackluster and/or I'm just bad at using it 🥲

Otherwise, the EuiDataGrid component here would have printed less, but because the output is an array, it's completely getting skipped by the serializer

}
};

export const fixIndentation = (children: string, startingIndentLevel = 1) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As an aside, this function was probably unnecessary for the spike and super extra but I'm lowkey pretty proud of this indentation logic 😂

@cee-chen
Copy link
Contributor Author

Spike done - we won't be moving forward with this approach. One idea for a different approach would be to try to evangelize Kibana to use fewer snapshots instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants