Skip to content
This repository has been archived by the owner on Mar 5, 2022. It is now read-only.

Commit

Permalink
fix: use display name of the component (#92)
Browse files Browse the repository at this point in the history
* Fixing #91

* Adding tests for React.memo
* Adding tests for React.forwardRef
* Adding basic hook tests

* Fixing some issues

* Fixing bad .type property in get where the selector is a function
* Ensuring all specs pass
* Adding stub for hooks spec for the time being...
  • Loading branch information
atomicpages authored and bahmutov committed Jun 5, 2019
1 parent aca9d68 commit 721f55c
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 2 deletions.
13 changes: 13 additions & 0 deletions cypress/integration/counter-with-hooks.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="cypress" />
/// <reference types="../../lib" />

import React from 'react'
import CounterWithHooks from '../../src/counter-with-hooks.jsx'

/* eslint-env mocha */
describe('CounterWithHooks component', function () {
it.skip('works', function () {
cy.mount(<CounterWithHooks initialCount={3} />)
cy.contains('3')
})
})
21 changes: 21 additions & 0 deletions cypress/integration/forward-ref.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference types="cypress" />
/// <reference types="../../lib" />

import React from 'react'
import Button from '../../src/forward-ref.jsx'

/* eslint-env mocha */
describe('Button component', function () {
it('works', function () {
cy.mount(<Button>Hello, World</Button>)
cy.contains('Hello, World')
})

it('forwards refs as expected', function () {
const ref = React.createRef();

cy.mount(<Button className="testing" ref={ref}>Hello, World</Button>);
expect(ref).to.have.property('current');
// expect(ref.current).not.be.null;
})
})
13 changes: 13 additions & 0 deletions cypress/integration/pure-component.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="cypress" />
/// <reference types="../../lib" />

import React from 'react'
import Button from '../../src/pure-component.jsx'

/* eslint-env mocha */
describe('Button pure component', function () {
it('works', function () {
cy.mount(<Button>Hello</Button>)
cy.contains('Hello')
})
})
52 changes: 52 additions & 0 deletions lib/getDisplayName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/// <reference path="./index.d.ts" />

const cachedDisplayNames: WeakMap<JSX, string> = new WeakMap();

/**
* Gets the display name of the component when possible.
* @param type {JSX} The type object returned from creating the react element.
* @param fallbackName {string} The alias, or fallback name to use when the name cannot be derived.
* @link https://github.com/facebook/react-devtools/blob/master/backend/getDisplayName.js
*/
export default function getDisplayName(type: JSX, fallbackName: string = 'Unknown'): string {
const nameFromCache = cachedDisplayNames.get(type)

if (nameFromCache != null) {
return nameFromCache
}

let displayName: string

// The displayName property is not guaranteed to be a string.
// It's only safe to use for our purposes if it's a string.
// github.com/facebook/react-devtools/issues/803
if (typeof type.displayName === 'string') {
displayName = type.displayName
}

if (!displayName) {
displayName = type.name || fallbackName
}

// Facebook-specific hack to turn "Image [from Image.react]" into just "Image".
// We need displayName with module name for error reports but it clutters the DevTools.
const match = displayName.match(/^(.*) \[from (.*)\]$/)

if (match) {
const componentName = match[1]
const moduleName = match[2]

if (componentName && moduleName) {
if (
moduleName === componentName ||
moduleName.startsWith(componentName + '.')
) {
displayName = componentName
}
}
}

cachedDisplayNames.set(type, displayName)

return displayName
}
10 changes: 10 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ interface ReactModule {
source: string
}

/**
* The `type` property from the transpiled JSX object.
* @example
* const { type } = React.createElement('div', null, 'Hello')
* const { type } = <div>Hello</div>
*/
interface JSX extends Function {
displayName: string
}

declare namespace Cypress {
interface Cypress {
stylesCache: any
Expand Down
7 changes: 5 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/// <reference path="./index.d.ts" />

import getDisplayName from './getDisplayName';

// having weak reference to styles prevents garbage collection
// and "losing" styles when the next test starts
const stylesCache = new Map()
Expand Down Expand Up @@ -106,7 +108,7 @@ Cypress.Commands.add('copyComponentStyles', component => {
**/
export const mount = (jsx, alias) => {
// Get the display name property via the component constructor
const displayname = alias || jsx.type.prototype.constructor.name
const displayname = getDisplayName(jsx.type, alias)

let cmd

Expand Down Expand Up @@ -165,7 +167,8 @@ Cypress.Commands.overwrite('get', (originalFn, selector, options) => {
}
case 'function':
// If attempting to use the component name without JSX (testing in .js/.ts files)
const displayname = selector.prototype.constructor.name
// const displayname = selector.prototype.constructor.name
const displayname = getDisplayName(selector);
return originalFn(`@${displayname}`, options)
default:
return originalFn(selector, options)
Expand Down
18 changes: 18 additions & 0 deletions src/counter-with-hooks.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

export default function CounterWithHooks({ initialCount = 0 }) {
const [count, setCount] = React.useState(initialCount);

const handleCountIncrement = React.useCallback(() => {
setCount(count + 1);
}, [count]);

return (
<>
<div className="counter">
{count}
</div>
<button onClick={handleCountIncrement}>+</button>
</>
)
}
5 changes: 5 additions & 0 deletions src/forward-ref.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

const Button = React.forwardRef(({ children, ...rest }, ref) => <button {...rest} ref={ref}>{children}</button>);

export default Button;
7 changes: 7 additions & 0 deletions src/pure-component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';

const Button = ({ children, ...rest }) => {
return <button {...rest}>{children}</button>;
};

export default React.memo(Button);

0 comments on commit 721f55c

Please sign in to comment.