diff --git a/cypress/integration/counter-with-hooks.spec.js b/cypress/integration/counter-with-hooks.spec.js
new file mode 100644
index 00000000..15ca35c3
--- /dev/null
+++ b/cypress/integration/counter-with-hooks.spec.js
@@ -0,0 +1,13 @@
+///
+///
+
+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()
+ cy.contains('3')
+ })
+})
diff --git a/cypress/integration/forward-ref.spec.js b/cypress/integration/forward-ref.spec.js
new file mode 100644
index 00000000..fd9b8b00
--- /dev/null
+++ b/cypress/integration/forward-ref.spec.js
@@ -0,0 +1,21 @@
+///
+///
+
+import React from 'react'
+import Button from '../../src/forward-ref.jsx'
+
+/* eslint-env mocha */
+describe('Button component', function () {
+ it('works', function () {
+ cy.mount()
+ cy.contains('Hello, World')
+ })
+
+ it('forwards refs as expected', function () {
+ const ref = React.createRef();
+
+ cy.mount();
+ expect(ref).to.have.property('current');
+ // expect(ref.current).not.be.null;
+ })
+})
diff --git a/cypress/integration/pure-component.spec.js b/cypress/integration/pure-component.spec.js
new file mode 100644
index 00000000..14bdd913
--- /dev/null
+++ b/cypress/integration/pure-component.spec.js
@@ -0,0 +1,13 @@
+///
+///
+
+import React from 'react'
+import Button from '../../src/pure-component.jsx'
+
+/* eslint-env mocha */
+describe('Button pure component', function () {
+ it('works', function () {
+ cy.mount()
+ cy.contains('Hello')
+ })
+})
diff --git a/lib/getDisplayName.ts b/lib/getDisplayName.ts
new file mode 100644
index 00000000..defcf3da
--- /dev/null
+++ b/lib/getDisplayName.ts
@@ -0,0 +1,52 @@
+///
+
+const cachedDisplayNames: WeakMap = 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
+}
\ No newline at end of file
diff --git a/lib/index.d.ts b/lib/index.d.ts
index 147c7846..70029182 100644
--- a/lib/index.d.ts
+++ b/lib/index.d.ts
@@ -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 } = Hello
+ */
+interface JSX extends Function {
+ displayName: string
+}
+
declare namespace Cypress {
interface Cypress {
stylesCache: any
diff --git a/lib/index.ts b/lib/index.ts
index 9d06c735..7d7bab0a 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -1,5 +1,7 @@
///
+import getDisplayName from './getDisplayName';
+
// having weak reference to styles prevents garbage collection
// and "losing" styles when the next test starts
const stylesCache = new Map()
@@ -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
@@ -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)
diff --git a/src/counter-with-hooks.jsx b/src/counter-with-hooks.jsx
new file mode 100644
index 00000000..5c7a3815
--- /dev/null
+++ b/src/counter-with-hooks.jsx
@@ -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 (
+ <>
+
+ {count}
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/forward-ref.jsx b/src/forward-ref.jsx
new file mode 100644
index 00000000..18ed8ddf
--- /dev/null
+++ b/src/forward-ref.jsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+const Button = React.forwardRef(({ children, ...rest }, ref) => );
+
+export default Button;
\ No newline at end of file
diff --git a/src/pure-component.jsx b/src/pure-component.jsx
new file mode 100644
index 00000000..bcd2f812
--- /dev/null
+++ b/src/pure-component.jsx
@@ -0,0 +1,7 @@
+import React from 'react';
+
+const Button = ({ children, ...rest }) => {
+ return ;
+};
+
+export default React.memo(Button);
\ No newline at end of file