Skip to content
This repository was archived by the owner on Jan 10, 2025. It is now read-only.

adding enzyme-like .debug() feature to Node instances#1088

Merged
patsissons merged 1 commit intomasterfrom
react-testing-debug-print
Oct 8, 2019
Merged

adding enzyme-like .debug() feature to Node instances#1088
patsissons merged 1 commit intomasterfrom
react-testing-debug-print

Conversation

@patsissons
Copy link
Contributor

@patsissons patsissons commented Oct 2, 2019

Description

Fixes (issue #) do we have an issue for this???

Similar to how enzyme handles .debug(), we can now emit mounted structure (and sub-structure) using wrapper.debug() or wrapper.find(Foo)!.debug().

increase (or decrease) verbosity of the props object expansion by changing the verbosity option (default is 1, wrapper.debug(3)). limit how deep to walk the graph by changing the depth option (default is undefined, walk the entire graph). Use either position parameters (wrapper.debug(verbosity, depth)) or a single options object (wrapper.debug({depth, verbosity}))

Test it out in your project using the debug console, wrapper.findWhere((type) => type && type.name === 'Breadcrumbs')!.debug(5)

<Header breadcrumbs={[{"content": "Settings", "url": "/admin/settings"}]} title="Capital" separator polaris={{"appBridge": undefined, "intl": {"translate": [Function anonymous], "translation": [Object]}, "link": undefined, "scrollLockManager": {"locked": false, "scrollLocks": 0}, "stickyManager": {"handleResize": [Function debounced], "handleScroll": [Function debounced], "stickyItems": [Array], "stuckItems": [Array], "topBarOffset": 0}, "theme": {"logo": null}}}>
  <div>
    <div>
      <div>
        <Breadcrumbs breadcrumbs={[{"content": "Settings", "url": "/admin/settings"}]}>
          <nav role="navigation">
            <Component url="/admin/settings" onMouseUp={[Function handleMouseUpByBlurring]}>
              <Component url="/admin/settings" onMouseUp={[Function handleMouseUpByBlurring]}>
                <a onMouseUp={[Function handleMouseUpByBlurring]} href="/admin/settings">
                  <span>
                    <Icon source={[Function SvgChevronLeftMinor]}>
                      <span>
                        <SvgChevronLeftMinor focusable="false">
                          <svg viewBox="0 0 20 20" focusable="false" />
                        </SvgChevronLeftMinor>
                      </span>
                    </Icon>
                  </span>
                  <span />
                </a>
              </Component>
            </Component>
          </nav>
        </Breadcrumbs>
      </div>
    </div>
    <div>
      <div>
        <Title title="Foo">
          <div>
            <div>
              <div>
                <DisplayText size="large" element="h1">
                  <h1 />
                </DisplayText>
              </div>
            </div>
          </div>
        </Title>
      </div>
    </div>
    <EventListener event="resize" handler={[Function debounced]} passive />
  </div>
</Header>

I'm open to changing the name from .debug(), but maybe we want to keep it the same (or at least an alias to it) for posterity?

Type of change

  • Minor: New feature (non-breaking change which adds functionality)

Checklist

  • I have added a changelog entry, prefixed by the type of change noted above

@patsissons patsissons force-pushed the react-testing-debug-print branch from e4c8d7d to 2cffa04 Compare October 2, 2019 23:38
): MaybeFunctionReturnType<NonNullable<Props[K]>>;
triggerKeypath<T = unknown>(keypath: string, ...args: unknown[]): T;

debug(maxDepth?: number): string;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

could also be handy to have an omitChildren flag.

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 maxDepth is a poor choice for a name, it really represents prop object expansion depth. perhaps a better approach is {propsDepth?: number; childDepth?: number}?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure when I'd want those two to be different values, or if i did that I'd remember that as an option. A single value for both feels more intuitive.

Copy link
Contributor Author

@patsissons patsissons Oct 3, 2019

Choose a reason for hiding this comment

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

i can see some legit use cases.

I want to see max detail for all props on a single component with no children
wrapper.findWhere(({type}) => type && type.name === 'Header').debug({verbosity: 9, depth: 0})

Expand to see a rather large expansion of props structure
<Header
  breadcrumbs={[{content: 'Bar', url: '/foo/bar'}]}
  title="Foo"
  separator
  polaris={{
    appBridge: undefined,
    intl: {
      translate: ['Function anonymous'],
      translation: {
        Polaris: {
          ActionMenu: {RollupActions: {rollupButton: 'Actions'}},
          Autocomplete: {spinnerAccessibilityLabel: 'Loading'},
          Avatar: {
            label: 'Avatar',
            labelWithInitials: 'Avatar with initials {initials}',
          },
          Badge: {
            PROGRESS_LABELS: {
              complete: 'Complete',
              incomplete: 'Incomplete',
              partiallyComplete: 'Partially complete',
            },
            STATUS_LABELS: {
              attention: 'Attention',
              info: 'Info',
              new: 'New',
              success: 'Success',
              warning: 'Warning',
            },
          },
          Button: {spinnerAccessibilityLabel: 'Loading'},
          Common: {
            cancel: 'Cancel',
            checkbox: 'checkbox',
            clear: 'Clear',
            close: 'Close',
            more: 'More',
            newWindowAccessibilityHint: '(opens a new window)',
            submit: 'Submit',
            undo: 'Undo',
          },
          ContextualSaveBar: {discard: 'Discard', save: 'Save'},
          DataTable: {
            navAccessibilityLabel:
              'Scroll table {direction} one column',
            sortAccessibilityLabel: 'sort {direction} by',
            totalsRowHeading: 'Totals',
          },
          DatePicker: {
            daysAbbreviated: {
              friday: 'Fr',
              monday: 'Mo',
              saturday: 'Sa',
              sunday: 'Su',
              thursday: 'Th',
              tuesday: 'Tu',
              wednesday: 'We',
            },
            months: {
              april: 'April',
              august: 'August',
              december: 'December',
              february: 'February',
              january: 'January',
              july: 'July',
              june: 'June',
              march: 'March',
              may: 'May',
              november: 'November',
              october: 'October',
              september: 'September',
            },
            nextMonth: 'Show next month, {nextMonth} {nextYear}',
            previousMonth:
              'Show previous month, {previousMonthName} {showPreviousYear}',
            today: 'Today ',
          },
          DiscardConfirmationModal: {
            message:
              'If you discard changes, you’ll delete any edits you made since you last saved.',
            primaryAction: 'Discard changes',
            secondaryAction: 'Continue editing',
            title: 'Discard all unsaved changes',
          },
          DropZone: {
            FileUpload: {
              actionHintFile: 'or drop files to upload',
              actionHintImage: 'or drop images to upload',
              actionTitleFile: 'Add file',
              actionTitleImage: 'Add image',
              label: 'Upload file',
            },
            errorOverlayTextFile: 'File type is not valid',
            errorOverlayTextImage: 'Image type is not valid',
            overlayTextFile: 'Drop file to upload',
            overlayTextImage: 'Drop image to upload',
          },
          EmptySearchResult: {altText: 'Empty search results'},
          Filters: {
            cancel: 'Cancel',
            clear: 'Clear',
            clearAllFilters: 'Clear all filters',
            clearLabel: 'Clear {filterName}',
            done: 'Done',
            filter: 'Filter {resourceName}',
            moreFilters: 'More filters',
            noFiltersApplied: 'No filters applied',
          },
          Frame: {
            Navigation: {
              closeMobileNavigationLabel: 'Close navigation',
            },
            skipToContent: 'Skip to content',
          },
          Icon: {
            backdropWarning:
              'The {color} icon doesn’t accept backdrops. The icon colors that have backdrops are: {colorsWithBackDrops}',
          },
          Modal: {
            iFrameTitle: 'body markup',
            modalWarning:
              'These required properties are missing from Modal: {missingProps}',
          },
          Pagination: {
            next: 'Next',
            pagination: 'Pagination',
            previous: 'Previous',
          },
          ProgressBar: {
            exceedWarningMessage:
              'Values passed to the progress prop shouldn’t exceed 100. Setting {progress} to 100.',
            negativeWarningMessage:
              'Values passed to the progress prop shouldn’t be negative. Resetting {progress} to 0.',
          },
          ResourceList: {
            BulkActions: {
              actionsActivatorLabel: 'Actions',
              moreActionsActivatorLabel: 'More actions',
              warningMessage:
                'To provide a better user experience. There should only be a maximum of {maxPromotedActions} promoted actions.',
            },
            DateSelector: {
              FilterLabelForValue: {
                coming_month: 'next month',
                coming_quarter: 'in the next 3 months',
                coming_week: 'next week',
                coming_year: 'in the next year',
                on_or_after: 'after {date}',
                on_or_before: 'before {date}',
                past_month: 'in the last month',
                past_quarter: 'in the last 3 months',
                past_week: 'in the last week',
                past_year: 'in the last year',
              },
              SelectOptions: {
                ComingMonth: 'next month',
                ComingQuarter: 'in the next 3 months',
                ComingWeek: 'next week',
                ComingYear: 'in the next year',
                OnOrAfter: 'on or after',
                OnOrBefore: 'on or before',
                PastMonth: 'in the last month',
                PastQuarter: 'in the last 3 months',
                PastWeek: 'in the last week',
                PastYear: 'in the last year',
              },
              dateFilterLabel: 'Select a value',
              dateValueError: 'Match YYYY-MM-DD format',
              dateValueLabel: 'Date',
              dateValuePlaceholder: 'YYYY-MM-DD',
            },
            FilterControl: {
              textFieldLabel: 'Search {resourceNamePlural}',
            },
            FilterCreator: {
              addFilterButtonLabel: 'Add filter',
              filterButtonLabel: 'Filter',
              selectFilterKeyPlaceholder: 'Select a filter…',
              showAllWhere: 'Show all {resourceNamePlural} where:',
            },
            FilterValueSelector: {
              selectFilterValuePlaceholder: 'Select a filter…',
            },
            Item: {
              actionsDropdown: 'Actions dropdown',
              actionsDropdownLabel:
                'Actions for {accessibilityLabel}',
              viewItem: 'View details for {itemName}',
            },
            a11yCheckboxDeselectAllMultiple:
              'Deselect all {itemsLength} {resourceNamePlural}',
            a11yCheckboxDeselectAllSingle:
              'Deselect {resourceNameSingular}',
            a11yCheckboxSelectAllMultiple:
              'Select all {itemsLength} {resourceNamePlural}',
            a11yCheckboxSelectAllSingle:
              'Select {resourceNameSingular}',
            allItemsSelected:
              'All {itemsLength}+ {resourceNamePlural} in your store are selected.',
            ariaLivePlural: '{itemsLength} items',
            ariaLiveSingular: '{itemsLength} item',
            defaultItemPlural: 'items',
            defaultItemSingular: 'item',
            emptySearchResultDescription:
              'Try changing the filters or search term',
            emptySearchResultTitle: 'No {resourceNamePlural} found',
            loading: 'Loading {resource}',
            selectAllItems:
              'Select all {itemsLength}+ {resourceNamePlural} in your store',
            selectButtonText: 'Select',
            selected: '{selectedItemsCount} selected',
            showing: 'Showing {itemsCount} {resource}',
            sortingLabel: 'Sort by',
          },
          SkeletonPage: {loadingLabel: 'Page loading'},
          Spinner: {
            warningMessage:
              'The color {color} is not meant to be used on {size} spinners. The colors available on large spinners are: {colors}',
          },
          Tabs: {toggleTabsLabel: 'More tabs'},
          Tag: {ariaLabel: 'Remove {children}'},
          TextField: {
            characterCount: '{count} characters',
            characterCountWithMaxLength:
              '{count} of {limit} characters used',
          },
          TopBar: {
            SearchField: {
              clearButtonLabel: 'Clear',
              search: 'Search',
            },
            toggleMenuLabel: 'Toggle menu',
          },
        },
      },
    },
    link: undefined,
    scrollLockManager: {locked: false, scrollLocks: 0},
    stickyManager: {
      handleResize: ['Function debounced'],
      handleScroll: ['Function debounced'],
      stickyItems: [],
      stuckItems: [],
      topBarOffset: 0,
    },
    theme: {logo: null},
  }}
>
  {/* <1 child... /> */}
</Header>

Copy link
Member

@BPScott BPScott Oct 3, 2019

Choose a reason for hiding this comment

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

Ah that makes more sense, thanks! I think i'd go for a 2 args approach with some sensible-ish defaults rather than an object. It feels like seeing more children while not going that deep into the props might be a decent base.debug(childrenMaxDepth = 5, propsMaxDepth = 1).

It's at this point that I mutter something something Polaris being on a warpath to kill our WithAppProvider component (the only HoC remaining in v4), and we're down to <15 usages, but that won't help the reset of web being very HoC happy.

@patsissons patsissons force-pushed the react-testing-debug-print branch 4 times, most recently from 6293ed0 to 3cc3021 Compare October 3, 2019 16:09
@patsissons patsissons marked this pull request as ready for review October 3, 2019 16:10
Copy link
Member

@lemonmade lemonmade left a comment

Choose a reason for hiding this comment

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

Definitely looks like a great feature 👍

}
}

function printElement(element: Element<any>, options: DebugOptions, level = 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there no existing serializer for a react element we could use?

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 there is i didn't encounter one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

looks like are a few options:

any of these could work, but really there isn't much code here so i'm not sure how interested i am in pulling in another dependency. the renderToStaticMarkup is probably a bit basic, pretty-format could be reasonable though if it's already present in the package dependences?

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 tried out pretty-format and it was a no go, we would need to change how we are rendering nodes which isn't really in the scope of this feature. The other options i don't feel make sense to attempt.

Copy link
Contributor

@cartogram cartogram left a comment

Choose a reason for hiding this comment

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

I love that you added this. I had the same idea but you beat me to it! 🙂

@patsissons patsissons force-pushed the react-testing-debug-print branch 3 times, most recently from 6a9ba4c to b1768b8 Compare October 4, 2019 22:41
Copy link
Member

@lemonmade lemonmade left a comment

Choose a reason for hiding this comment

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

If you feel the print won’t be an ongoing maintenance burden then 👍

@patsissons patsissons force-pushed the react-testing-debug-print branch 2 times, most recently from da22127 to 60477a5 Compare October 8, 2019 15:58
@patsissons patsissons force-pushed the react-testing-debug-print branch from 60477a5 to d932ed9 Compare October 8, 2019 16:03
@patsissons patsissons merged commit cbbce33 into master Oct 8, 2019
@patsissons patsissons temporarily deployed to production October 8, 2019 20:10 Inactive
@michenly michenly deleted the react-testing-debug-print branch October 9, 2019 18:12
@marutypes marutypes temporarily deployed to gem October 23, 2019 21:05 Inactive
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants