From 3d62302bf05922edcef12ec08e5fbf190f6153f1 Mon Sep 17 00:00:00 2001 From: Vyrtsev Mikhail Date: Sat, 6 Jul 2019 18:48:25 +0300 Subject: [PATCH] add enzyme test renderer package --- frontend/app/@types/enzyme/enzyme.d.ts | 723 +++++++++++++++++++++++++ frontend/app/testUtils/index.ts | 5 + frontend/package-lock.json | 453 ++++++++++++++++ frontend/package.json | 4 + 4 files changed, 1185 insertions(+) create mode 100644 frontend/app/@types/enzyme/enzyme.d.ts diff --git a/frontend/app/@types/enzyme/enzyme.d.ts b/frontend/app/@types/enzyme/enzyme.d.ts new file mode 100644 index 00000000..97a4e355 --- /dev/null +++ b/frontend/app/@types/enzyme/enzyme.d.ts @@ -0,0 +1,723 @@ +/** + * THIS IS PATCHED VERSION OF @types/enzyme THAT ADJUSTED FOR PREACT + * ALL CREDIT GOES TO MAINTAINERS OF @types/enzyme + */ + +/* eslint-disable */ + +declare module 'enzyme' { + // Type definitions for Enzyme 3.10 + // Project: https://github.com/airbnb/enzyme + // Definitions by: Marian Palkus + // Cap3 + // Ivo Stratev + // jwbay + // huhuanming + // MartynasZilinskas + // Torgeir Hovden + // Martin Hochel + // Christian Rackerseder + // Mateusz SokoĊ‚a + // Braiden Cutforth + // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + // TypeScript Version: 3.1 + + /// + import { Component } from 'preact'; + + export type HTMLAttributes = any; + + class ReactElement {} + + export class ElementClass {} + + /* These are purposefully stripped down versions of React.ComponentClass and React.StatelessComponent. + * The optional static properties on them break overload ordering for wrapper methods if they're not + * all specified in the implementation. TS chooses the EnzymePropSelector overload and loses the generics + */ + export interface ComponentClass { + new (props: Props, context?: any): Component; + } + + export type StatelessComponent = (props: Props, context?: any) => JSX.Element | null; + + export type ComponentType = ComponentClass | StatelessComponent; + + /** + * Many methods in Enzyme's API accept a selector as an argument. Selectors in Enzyme can fall into one of the + * following three categories: + * + * 1. A Valid CSS Selector + * 2. A React Component Constructor + * 3. A React Component's displayName + * 4. A React Stateless component + * 5. A React component property map + */ + export interface EnzymePropSelector { + [key: string]: any; + } + export type EnzymeSelector = string | StatelessComponent | ComponentClass | EnzymePropSelector; + + export type Intercepter = (intercepter: T) => void; + + export interface CommonWrapper

> { + /** + * Returns a new wrapper with only the nodes of the current wrapper that, when passed into the provided predicate function, return true. + */ + filterWhere(predicate: (wrapper: this) => boolean): this; + + /** + * Returns whether or not the current wrapper has a node anywhere in it's render tree that looks like the one passed in. + */ + contains(node: ReactElement | ReactElement[] | string): boolean; + + /** + * Returns whether or not a given react element exists in the shallow render tree. + */ + containsMatchingElement(node: ReactElement | ReactElement[]): boolean; + + /** + * Returns whether or not all the given react elements exists in the shallow render tree + */ + containsAllMatchingElements(nodes: ReactElement[] | ReactElement[][]): boolean; + + /** + * Returns whether or not one of the given react elements exists in the shallow render tree. + */ + containsAnyMatchingElements(nodes: ReactElement[] | ReactElement[][]): boolean; + + /** + * Returns whether or not the current render tree is equal to the given node, based on the expected value. + */ + equals(node: ReactElement): boolean; + + /** + * Returns whether or not a given react element matches the shallow render tree. + */ + matchesElement(node: ReactElement): boolean; + + /** + * Returns whether or not the current node has a className prop including the passed in class name. + */ + hasClass(className: string | RegExp): boolean; + + /** + * Invokes a function prop. + * @param invokePropName The function prop to call. + * @param ...args The argments to the invokePropName function + * @returns The value of the function. + */ + invoke< + K extends NonNullable<{ [K in keyof P]: P[K] extends ((...arg: any[]) => void) | undefined ? K : never }[keyof P]> + >( + invokePropName: K + ): P[K]; + + /** + * Returns whether or not the current node matches a provided selector. + */ + is(selector: EnzymeSelector): boolean; + + /** + * Returns whether or not the current node is empty. + * @deprecated Use .exists() instead. + */ + isEmpty(): boolean; + + /** + * Returns whether or not the current node exists. + */ + exists(selector?: EnzymeSelector): boolean; + + /** + * Returns a new wrapper with only the nodes of the current wrapper that don't match the provided selector. + * This method is effectively the negation or inverse of filter. + */ + not(selector: EnzymeSelector): this; + + /** + * Returns a string of the rendered text of the current render tree. This function should be looked at with + * skepticism if being used to test what the actual HTML output of the component will be. If that is what you + * would like to test, use enzyme's render function instead. + * + * Note: can only be called on a wrapper of a single node. + */ + text(): string; + + /** + * Returns a string of the rendered HTML markup of the current render tree. + * + * Note: can only be called on a wrapper of a single node. + */ + html(): string; + + /** + * Returns the node at a given index of the current wrapper. + */ + get(index: number): ReactElement; + + /** + * Returns the wrapper's underlying node. + */ + getNode(): ReactElement; + + /** + * Returns the wrapper's underlying nodes. + */ + getNodes(): ReactElement[]; + + /** + * Returns the wrapper's underlying node. + */ + getElement(): ReactElement; + + /** + * Returns the wrapper's underlying node. + */ + getElements(): ReactElement[]; + + /** + * Returns the outer most DOMComponent of the current wrapper. + */ + getDOMNode(): T; + + /** + * Returns a wrapper around the node at a given index of the current wrapper. + */ + at(index: number): this; + + /** + * Reduce the set of matched nodes to the first in the set. + */ + first(): this; + + /** + * Reduce the set of matched nodes to the last in the set. + */ + last(): this; + + /** + * Returns a new wrapper with a subset of the nodes of the original wrapper, according to the rules of `Array#slice`. + */ + slice(begin?: number, end?: number): this; + + /** + * Taps into the wrapper method chain. Helpful for debugging. + */ + tap(intercepter: Intercepter): this; + + /** + * Returns the state hash for the root node of the wrapper. Optionally pass in a prop name and it will return just that value. + */ + state(): S; + state(key: K): S[K]; + state(key: string): T; + + /** + * Returns the context hash for the root node of the wrapper. Optionally pass in a prop name and it will return just that value. + */ + context(): any; + context(key: string): T; + + /** + * Returns the props hash for the current node of the wrapper. + * + * NOTE: can only be called on a wrapper of a single node. + */ + props(): P; + + /** + * Returns the prop value for the node of the current wrapper with the provided key. + * + * NOTE: can only be called on a wrapper of a single node. + */ + prop(key: K): P[K]; + prop(key: string): T; + + /** + * Returns the key value for the node of the current wrapper. + * NOTE: can only be called on a wrapper of a single node. + */ + key(): string; + + /** + * Simulate events. + * Returns itself. + * @param args? + */ + simulate(event: string, ...args: any[]): this; + + /** + * Used to simulate throwing a rendering error. Pass an error to throw. + * Returns itself. + * @param error + */ + simulateError(error: any): this; + + /** + * A method to invoke setState() on the root component instance similar to how you might in the definition of + * the component, and re-renders. This method is useful for testing your component in hard to achieve states, + * however should be used sparingly. If possible, you should utilize your component's external API in order to + * get it into whatever state you want to test, in order to be as accurate of a test as possible. This is not + * always practical, however. + * Returns itself. + * + * NOTE: can only be called on a wrapper instance that is also the root instance. + */ + setState(state: Pick, callback?: () => void): this; + + /** + * A method that sets the props of the root component, and re-renders. Useful for when you are wanting to test + * how the component behaves over time with changing props. Calling this, for instance, will call the + * componentWillReceiveProps lifecycle method. + * + * Similar to setState, this method accepts a props object and will merge it in with the already existing props. + * Returns itself. + * + * NOTE: can only be called on a wrapper instance that is also the root instance. + */ + setProps(props: Pick, callback?: () => void): this; + + /** + * A method that sets the context of the root component, and re-renders. Useful for when you are wanting to + * test how the component behaves over time with changing contexts. + * Returns itself. + * + * NOTE: can only be called on a wrapper instance that is also the root instance. + */ + setContext(context: any): this; + + /** + * Gets the instance of the component being rendered as the root node passed into shallow(). + * + * NOTE: can only be called on a wrapper instance that is also the root instance. + */ + instance(): C; + + /** + * Forces a re-render. Useful to run before checking the render output if something external may be updating + * the state of the component somewhere. + * Returns itself. + * + * NOTE: can only be called on a wrapper instance that is also the root instance. + */ + update(): this; + + /** + * Returns an html-like string of the wrapper for debugging purposes. Useful to print out to the console when + * tests are not passing when you expect them to. + */ + debug(): string; + + /** + * Returns the name of the current node of the wrapper. + */ + name(): string; + + /** + * Iterates through each node of the current wrapper and executes the provided function with a wrapper around + * the corresponding node passed in as the first argument. + * + * Returns itself. + * @param fn A callback to be run for every node in the collection. Should expect a ShallowWrapper as the first + * argument, and will be run with a context of the original instance. + */ + forEach(fn: (wrapper: this, index: number) => any): this; + + /** + * Maps the current array of nodes to another array. Each node is passed in as a ShallowWrapper to the map + * function. + * Returns an array of the returned values from the mapping function.. + * @param fn A mapping function to be run for every node in the collection, the results of which will be mapped + * to the returned array. Should expect a ShallowWrapper as the first argument, and will be run + * with a context of the original instance. + */ + map(fn: (wrapper: this, index: number) => V): V[]; + + /** + * Applies the provided reducing function to every node in the wrapper to reduce to a single value. Each node + * is passed in as a ShallowWrapper, and is processed from left to right. + */ + reduce(fn: (prevVal: R, wrapper: this, index: number) => R, initialValue?: R): R; + + /** + * Applies the provided reducing function to every node in the wrapper to reduce to a single value. + * Each node is passed in as a ShallowWrapper, and is processed from right to left. + */ + reduceRight(fn: (prevVal: R, wrapper: this, index: number) => R, initialValue?: R): R; + + /** + * Returns whether or not any of the nodes in the wrapper match the provided selector. + */ + some(selector: EnzymeSelector): boolean; + + /** + * Returns whether or not any of the nodes in the wrapper pass the provided predicate function. + */ + someWhere(fn: (wrapper: this) => boolean): boolean; + + /** + * Returns whether or not all of the nodes in the wrapper match the provided selector. + */ + every(selector: EnzymeSelector): boolean; + + /** + * Returns whether or not all of the nodes in the wrapper pass the provided predicate function. + */ + everyWhere(fn: (wrapper: this) => boolean): boolean; + + /** + * Returns true if renderer returned null + */ + isEmptyRender(): boolean; + + /** + * Renders the component to static markup and returns a Cheerio wrapper around the result. + */ + render(): Cheerio; + + /** + * Returns the type of the current node of this wrapper. If it's a composite component, this will be the + * component constructor. If it's native DOM node, it will be a string of the tag name. + * + * Note: can only be called on a wrapper of a single node. + */ + type(): string | ComponentClass

| StatelessComponent

; + + length: number; + } + + export type Parameters = T extends (...args: infer A) => any ? A : never; + + // tslint:disable-next-line no-empty-interface + export interface ShallowWrapper

extends CommonWrapper {} + export class ShallowWrapper

{ + constructor(nodes: JSX.Element[] | JSX.Element, root?: ShallowWrapper, options?: ShallowRendererProps); + shallow(options?: ShallowRendererProps): ShallowWrapper; + unmount(): this; + + /** + * Find every node in the render tree that matches the provided selector. + * @param selector The selector to match. + */ + find(statelessComponent: StatelessComponent): ShallowWrapper; + find(component: ComponentType): ShallowWrapper; + find(props: EnzymePropSelector): ShallowWrapper; + find(selector: string): ShallowWrapper; + + /** + * Removes nodes in the current wrapper that do not match the provided selector. + * @param selector The selector to match. + */ + filter(statelessComponent: StatelessComponent): ShallowWrapper; + filter(component: ComponentType): ShallowWrapper; + filter(props: EnzymePropSelector | string): ShallowWrapper; + + /** + * Finds every node in the render tree that returns true for the provided predicate function. + */ + findWhere(predicate: (wrapper: ShallowWrapper) => boolean): ShallowWrapper; + + /** + * Returns a new wrapper with all of the children of the node(s) in the current wrapper. Optionally, a selector + * can be provided and it will filter the children by this selector. + */ + children(statelessComponent: StatelessComponent): ShallowWrapper; + children(component: ComponentType): ShallowWrapper; + children(selector: string): ShallowWrapper; + children(props?: EnzymePropSelector): ShallowWrapper; + + /** + * Returns a new wrapper with child at the specified index. + */ + childAt(index: number): ShallowWrapper; + childAt(index: number): ShallowWrapper; + + /** + * Shallow render the one non-DOM child of the current wrapper, and return a wrapper around the result. + * NOTE: can only be called on wrapper of a single non-DOM component element node. + */ + dive( + options?: ShallowRendererProps + ): ShallowWrapper; + dive(options?: ShallowRendererProps): ShallowWrapper; + dive(options?: ShallowRendererProps): ShallowWrapper; + + /** + * Strips out all the not host-nodes from the list of nodes + * + * This method is useful if you want to check for the presence of host nodes + * (actually rendered HTML elements) ignoring the React nodes. + */ + hostNodes(): ShallowWrapper; + + /** + * Returns a wrapper around all of the parents/ancestors of the wrapper. Does not include the node in the + * current wrapper. Optionally, a selector can be provided and it will filter the parents by this selector. + * + * Note: can only be called on a wrapper of a single node. + */ + parents(statelessComponent: StatelessComponent): ShallowWrapper; + parents(component: ComponentType): ShallowWrapper; + parents(selector: string): ShallowWrapper; + parents(props?: EnzymePropSelector): ShallowWrapper; + + /** + * Returns a wrapper of the first element that matches the selector by traversing up through the current node's + * ancestors in the tree, starting with itself. + * + * Note: can only be called on a wrapper of a single node. + */ + closest(statelessComponent: StatelessComponent): ShallowWrapper; + closest(component: ComponentType): ShallowWrapper; + closest(props: EnzymePropSelector): ShallowWrapper; + closest(selector: string): ShallowWrapper; + + /** + * Returns a wrapper with the direct parent of the node in the current wrapper. + */ + parent(): ShallowWrapper; + + /** + * Returns a wrapper of the node rendered by the provided render prop. + */ + renderProp( + prop: PropName + ): (...params: Parameters) => ShallowWrapper; + + /** + * If a wrappingComponent was passed in options, + * this methods returns a ShallowWrapper around the rendered wrappingComponent. + * This ShallowWrapper can be used to update the wrappingComponent's props and state + */ + getWrappingComponent: () => ShallowWrapper; + } + + // tslint:disable-next-line no-empty-interface + export interface ReactWrapper

extends CommonWrapper {} + export class ReactWrapper

{ + constructor(nodes: JSX.Element | JSX.Element[], root?: ReactWrapper, options?: MountRendererProps); + + unmount(): this; + mount(): this; + + /** + * Returns a wrapper of the node that matches the provided reference name. + * + * NOTE: can only be called on a wrapper instance that is also the root instance. + */ + ref(refName: string): ReactWrapper; + ref(refName: string): ReactWrapper; + + /** + * Detaches the react tree from the DOM. Runs ReactDOM.unmountComponentAtNode() under the hood. + * + * This method will most commonly be used as a "cleanup" method if you decide to use the attachTo option in mount(node, options). + * + * The method is intentionally not "fluent" (in that it doesn't return this) because you should not be doing anything with this wrapper after this method is called. + * + * Using the attachTo is not generally recommended unless it is absolutely necessary to test something. + * It is your responsibility to clean up after yourself at the end of the test if you do decide to use it, though. + */ + detach(): void; + + /** + * Strips out all the not host-nodes from the list of nodes + * + * This method is useful if you want to check for the presence of host nodes + * (actually rendered HTML elements) ignoring the React nodes. + */ + hostNodes(): ReactWrapper; + + /** + * Find every node in the render tree that matches the provided selector. + * @param selector The selector to match. + */ + find(statelessComponent: StatelessComponent): ReactWrapper; + find(component: ComponentType): ReactWrapper; + find(props: EnzymePropSelector): ReactWrapper; + find(selector: string): ReactWrapper; + + /** + * Finds every node in the render tree that returns true for the provided predicate function. + */ + findWhere(predicate: (wrapper: ReactWrapper) => boolean): ReactWrapper; + + /** + * Removes nodes in the current wrapper that do not match the provided selector. + * @param selector The selector to match. + */ + filter(statelessComponent: StatelessComponent): ReactWrapper; + filter(component: ComponentType): ReactWrapper; + filter(props: EnzymePropSelector | string): ReactWrapper; + + /** + * Returns a new wrapper with all of the children of the node(s) in the current wrapper. Optionally, a selector + * can be provided and it will filter the children by this selector. + */ + children(statelessComponent: StatelessComponent): ReactWrapper; + children(component: ComponentType): ReactWrapper; + children(selector: string): ReactWrapper; + children(props?: EnzymePropSelector): ReactWrapper; + + /** + * Returns a new wrapper with child at the specified index. + */ + childAt(index: number): ReactWrapper; + childAt(index: number): ReactWrapper; + + /** + * Returns a wrapper around all of the parents/ancestors of the wrapper. Does not include the node in the + * current wrapper. Optionally, a selector can be provided and it will filter the parents by this selector. + * + * Note: can only be called on a wrapper of a single node. + */ + parents(statelessComponent: StatelessComponent): ReactWrapper; + parents(component: ComponentType): ReactWrapper; + parents(selector: string): ReactWrapper; + parents(props?: EnzymePropSelector): ReactWrapper; + + /** + * Returns a wrapper of the first element that matches the selector by traversing up through the current node's + * ancestors in the tree, starting with itself. + * + * Note: can only be called on a wrapper of a single node. + */ + closest(statelessComponent: StatelessComponent): ReactWrapper; + closest(component: ComponentType): ReactWrapper; + closest(props: EnzymePropSelector): ReactWrapper; + closest(selector: string): ReactWrapper; + + /** + * Returns a wrapper with the direct parent of the node in the current wrapper. + */ + parent(): ReactWrapper; + } + + export interface Lifecycles { + componentDidUpdate?: { + onSetState: boolean; + prevContext: boolean; + }; + getDerivedStateFromProps?: { hasShouldComponentUpdateBug: boolean } | boolean; + getChildContext?: { + calledByRenderer: boolean; + [key: string]: any; + }; + setState?: any; + // TODO Maybe some life cycle are missing + [lifecycleName: string]: any; + } + + export interface ShallowRendererProps { + // See https://github.com/airbnb/enzyme/blob/enzyme@3.10.0/docs/api/shallow.md#arguments + /** + * If set to true, componentDidMount is not called on the component, and componentDidUpdate is not called after + * setProps and setContext. Default to false. + */ + disableLifecycleMethods?: boolean; + /** + * Enable experimental support for full react lifecycle methods + */ + lifecycleExperimental?: boolean; + /** + * Context to be passed into the component + */ + context?: any; + /** + * The legacy enableComponentDidUpdateOnSetState option should be matched by + * `lifecycles: { componentDidUpdate: { onSetState: true } }`, for compatibility + */ + enableComponentDidUpdateOnSetState?: boolean; + /** + * the legacy supportPrevContextArgumentOfComponentDidUpdate option should be matched by + * `lifecycles: { componentDidUpdate: { prevContext: true } }`, for compatibility + */ + supportPrevContextArgumentOfComponentDidUpdate?: boolean; + lifecycles?: Lifecycles; + /** + * A component that will render as a parent of the node. + * It can be used to provide context to the node, among other things. + * See https://airbnb.io/enzyme/docs/api/ShallowWrapper/getWrappingComponent.html + * Note: wrappingComponent must render its children. + */ + wrappingComponent?: ComponentType; + /** + * Initial props to pass to the wrappingComponent if it is specified. + */ + wrappingComponentProps?: any; + /** + * If set to true, when rendering Suspense enzyme will replace all the lazy components in children + * with fallback element prop. Otherwise it won't handle fallback of lazy component. + * Default to true. Note: not supported in React < 16.6. + */ + suspenseFallback?: boolean; + adapter?: EnzymeAdapter; + /* TODO what are these doing??? */ + attachTo?: any; + hydrateIn?: any; + PROVIDER_VALUES?: any; + } + + export interface MountRendererProps { + /** + * Context to be passed into the component + */ + context?: {}; + /** + * DOM Element to attach the component to + */ + attachTo?: HTMLElement | null; + /** + * Merged contextTypes for all children of the wrapper + */ + childContextTypes?: {}; + } + + /** + * Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that + * your tests aren't indirectly asserting on behavior of child components. + */ + export function shallow( + node: ReactElement

, + options?: ShallowRendererProps + ): ShallowWrapper; + export function shallow

(node: ReactElement

, options?: ShallowRendererProps): ShallowWrapper; + export function shallow(node: ReactElement

, options?: ShallowRendererProps): ShallowWrapper; + + /** + * Mounts and renders a react component into the document and provides a testing wrapper around it. + */ + export function mount( + node: ReactElement

, + options?: MountRendererProps + ): ReactWrapper; + export function mount

(node: ReactElement

, options?: MountRendererProps): ReactWrapper; + export function mount(node: ReactElement

, options?: MountRendererProps): ReactWrapper; + + /** + * Render react components to static HTML and analyze the resulting HTML structure. + */ + export function render(node: ReactElement

, options?: any): Cheerio; + + // See https://github.com/airbnb/enzyme/blob/v3.10.0/packages/enzyme/src/EnzymeAdapter.js + export class EnzymeAdapter { + wrapWithWrappingComponent?: (node: ReactElement, options?: ShallowRendererProps) => any; + } + + /** + * Configure enzyme to use the correct adapter for the react version + * This is enabling the Enzyme configuration with adapters in TS + */ + export function configure(options: { + adapter: EnzymeAdapter; + // See https://github.com/airbnb/enzyme/blob/enzyme@3.10.0/docs/guides/migration-from-2-to-3.md#lifecycle-methods + // Actually, `{adapter:} & Pick` is more precise. However, + // in that case jsdoc won't be shown + /** + * If set to true, componentDidMount is not called on the component, and componentDidUpdate is not called after + * setProps and setContext. Default to false. + */ + disableLifecycleMethods?: boolean; + }): void; +} diff --git a/frontend/app/testUtils/index.ts b/frontend/app/testUtils/index.ts index 499a4600..073e9059 100644 --- a/frontend/app/testUtils/index.ts +++ b/frontend/app/testUtils/index.ts @@ -1,5 +1,10 @@ import 'jest-extended'; +import 'jest-enzyme'; import { StaticStore } from '@app/common/static_store'; +import { configure } from 'enzyme'; +import PreactAdapter from 'enzyme-adapter-preact-pure'; + +configure({ adapter: new PreactAdapter() }); require('document-register-element/pony')(window); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2da8e00e..4403f26e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1495,6 +1495,15 @@ "@babel/types": "^7.3.0" } }, + "@types/cheerio": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.12.tgz", + "integrity": "sha512-aczowyAJNfrkBV+HS8DyAA87OnvkqGrrOmm5s7V6Jbgimzv/1ZoAy91cLJX8GQrUS60KufD7EIzA2LbK8HV4hg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/core-js": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-2.5.0.tgz", @@ -2133,6 +2142,28 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.flat": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz", + "integrity": "sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.10.0", + "function-bind": "^1.1.1" + } + }, + "array.prototype.flatmap": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.1.tgz", + "integrity": "sha512-i18e2APdsiezkcqDyZor78Pbfjfds3S94dG6dgIV2ZASJaUf1N0dz2tGdrmwrmlZuNUgxH+wz6Z0zYVH2c5xzQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.10.0", + "function-bind": "^1.1.1" + } + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -2961,6 +2992,65 @@ "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==", "dev": true }, + "cheerio": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", + "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", + "dev": true, + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.1", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + }, + "dependencies": { + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "chokidar": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", @@ -3020,6 +3110,12 @@ "safe-buffer": "^5.0.1" } }, + "circular-json-es6": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/circular-json-es6/-/circular-json-es6-2.0.2.tgz", + "integrity": "sha512-ODYONMMNb3p658Zv+Pp+/XPa5s6q7afhz3Tzyvo+VRh9WIrJ64J76ZC4GQxnlye/NesTn09jvOiuE8+xxfpwhQ==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3744,6 +3840,27 @@ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, + "deep-equal-ident": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz", + "integrity": "sha1-BvS4nlNxDNbOpKd4HHqVZkLejck=", + "dev": true, + "requires": { + "lodash.isequal": "^3.0" + }, + "dependencies": { + "lodash.isequal": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz", + "integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=", + "dev": true, + "requires": { + "lodash._baseisequal": "^3.0.0", + "lodash._bindcallback": "^3.0.0" + } + } + } + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -3911,6 +4028,12 @@ "path-type": "^3.0.0" } }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", + "dev": true + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -4124,6 +4247,81 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, + "enzyme": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.10.0.tgz", + "integrity": "sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg==", + "dev": true, + "requires": { + "array.prototype.flat": "^1.2.1", + "cheerio": "^1.0.0-rc.2", + "function.prototype.name": "^1.1.0", + "has": "^1.0.3", + "html-element-map": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-callable": "^1.1.4", + "is-number-object": "^1.0.3", + "is-regex": "^1.0.4", + "is-string": "^1.0.4", + "is-subset": "^0.1.1", + "lodash.escape": "^4.0.1", + "lodash.isequal": "^4.5.0", + "object-inspect": "^1.6.0", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4", + "object.values": "^1.0.4", + "raf": "^3.4.0", + "rst-selector-parser": "^2.2.3", + "string.prototype.trim": "^1.1.2" + } + }, + "enzyme-adapter-preact-pure": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-preact-pure/-/enzyme-adapter-preact-pure-2.0.0.tgz", + "integrity": "sha512-KeOEkzvSr2YraAZWKGWt5oVKHjIQNQ3ds3TIna97lIK+Hzfr9Y8ay8eD+tmBazU2aaf1MCqDTizGpCy0UGLSVg==", + "dev": true, + "requires": { + "array.prototype.flatmap": "^1.2.1", + "preact-render-to-string": "^4.1.0" + }, + "dependencies": { + "preact-render-to-string": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-4.1.0.tgz", + "integrity": "sha512-FlFBJRxo8z4cp6VsDmeYjIEx4ZK2clFJnKIvIj8K1IQCRm7ZgJ/SZ1+BotT86/Nc+V1pNtFabHoUi6gpjx5Pug==", + "dev": true, + "requires": { + "pretty-format": "^3.8.0" + } + }, + "pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha1-v77VbV6ad2ZF9LH/eqGjrE+jw4U=", + "dev": true + } + } + }, + "enzyme-matchers": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/enzyme-matchers/-/enzyme-matchers-7.0.2.tgz", + "integrity": "sha512-hsWb4Mw+F0eJ9q6ljTK2RqLwsrdoKfr5IXmKtDaTPeOEFXsVl6L2RX2REdevvn2KJfSxkcKaCGwPldOSc1l9tQ==", + "dev": true, + "requires": { + "circular-json-es6": "^2.0.1", + "deep-equal-ident": "^1.1.1" + } + }, + "enzyme-to-json": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.5.tgz", + "integrity": "sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA==", + "dev": true, + "requires": { + "lodash": "^4.17.4" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -5676,6 +5874,17 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz", + "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "is-callable": "^1.1.3" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -6059,6 +6268,23 @@ "wbuf": "^1.1.0" } }, + "html-element-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.0.1.tgz", + "integrity": "sha512-BZSfdEm6n706/lBfXKWa4frZRZcT5k1cOusw95ijZsHlI+GdgY0v95h6IzO3iIDf2ROwq570YTwqNPqHcNMozw==", + "dev": true, + "requires": { + "array-filter": "^1.0.0" + }, + "dependencies": { + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + } + } + }, "html-encoding-sniffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", @@ -6595,6 +6821,12 @@ "binary-extensions": "^1.0.0" } }, + "is-boolean-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz", + "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=", + "dev": true + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -6735,6 +6967,12 @@ } } }, + "is-number-object": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz", + "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=", + "dev": true + }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -6828,6 +7066,18 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz", + "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=", + "dev": true + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -7130,6 +7380,15 @@ "pretty-format": "^24.8.0" } }, + "jest-environment-enzyme": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/jest-environment-enzyme/-/jest-environment-enzyme-7.0.2.tgz", + "integrity": "sha512-cBBWX3ZCR4JJR6ml6cTYLH1fdBZ2QEbywY6yrkgTP6OZgE+Nh6Agu0g99uJha2F36+zRFS/xkk8AIEAJvLtzQQ==", + "dev": true, + "requires": { + "jest-environment-jsdom": "^24.0.0" + } + }, "jest-environment-jsdom": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", @@ -7157,6 +7416,17 @@ "jest-util": "^24.8.0" } }, + "jest-enzyme": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/jest-enzyme/-/jest-enzyme-7.0.2.tgz", + "integrity": "sha512-7N9YhFmyPkDcVyvifyWePkJaZQX/wON4D6uD8qmGSc2ZaXjj+fkEPMaZ1+Kqnp75FTHaJZ+NVigpmoQiys+J2w==", + "dev": true, + "requires": { + "enzyme-matchers": "^7.0.2", + "enzyme-to-json": "^3.3.0", + "jest-environment-enzyme": "^7.0.2" + } + }, "jest-extended": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.1.tgz", @@ -8123,12 +8393,82 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash._baseisequal": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz", + "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=", + "dev": true, + "requires": { + "lodash.isarray": "^3.0.0", + "lodash.istypedarray": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", + "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, + "lodash.istypedarray": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz", + "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -8527,6 +8867,12 @@ } } }, + "moo": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", + "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==", + "dev": true + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -8601,6 +8947,19 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "nearley": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz", + "integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "moo": "^0.4.3", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6", + "semver": "^5.4.1" + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -8881,6 +9240,18 @@ } } }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "dev": true + }, "object-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", @@ -8896,6 +9267,30 @@ "isobject": "^3.0.0" } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "object.fromentries": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", @@ -8937,6 +9332,18 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -10032,6 +10439,31 @@ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dev": true, + "requires": { + "performance-now": "^2.1.0" + } + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", + "dev": true + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", @@ -10498,6 +10930,16 @@ "inherits": "^2.0.1" } }, + "rst-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", + "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", + "dev": true, + "requires": { + "lodash.flattendeep": "^4.4.0", + "nearley": "^2.7.10" + } + }, "rsvp": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", @@ -11321,6 +11763,17 @@ "function-bind": "^1.0.2" } }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.0", + "function-bind": "^1.0.2" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 972e968a..084e343b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "@babel/plugin-transform-react-jsx": "^7.3.0", "@babel/preset-env": "^7.4.5", "@babel/preset-react": "^7.0.0", + "@types/cheerio": "^0.22.12", "@types/core-js": "^2.5.0", "@types/fetch-mock": "^7.3.1", "@types/jest": "^24.0.13", @@ -50,6 +51,8 @@ "copy-webpack-plugin": "^5.0.3", "css-loader": "^2.1.1", "document-register-element": "^1.13.2", + "enzyme": "^3.10.0", + "enzyme-adapter-preact-pure": "^2.0.0", "eslint": "^5.16.0", "eslint-config-prettier": "^4.3.0", "eslint-plugin-jsx-a11y": "^6.2.1", @@ -60,6 +63,7 @@ "html-webpack-plugin": "^3.2.0", "husky": "^2.4.0", "jest": "^24.8.0", + "jest-enzyme": "^7.0.2", "jest-extended": "^0.11.1", "jest-localstorage-mock": "^2.4.0", "lint-staged": "^8.1.7",