react-component-snapshot 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,38 @@
1
1
  # react-component-snapshot
2
2
 
3
- Transform a React component to match its snapshot in Jest
3
+ Transform a React component to match its snapshot in **Jest** with **Testing Library**
4
4
 
5
5
  ```javascript
6
6
  import snapshotOf from 'react-component-snapshot'
7
+ ```
8
+
9
+ ## With `@testing-library/react`
10
+
11
+ ```javascript
12
+ import {
13
+ render
14
+ } from '@testing-library/react'
7
15
 
8
- const {
9
- container: {
10
- firstElementChild: element
16
+ import snapshotOf from 'react-component-snapshot'
17
+
18
+ describe('snapshotOf', () => {
19
+ class Component extends React.Component {
20
+ render () {
21
+ return <div />
22
+ }
11
23
  }
12
- } = render(
13
- <Component />
14
- )
15
24
 
16
- expect(snapshotOf(element))
17
- .toMatchSnapshot()
25
+ it('matches the snapshot', () => {
26
+ const {
27
+ container: {
28
+ firstElementChild: element
29
+ }
30
+ } = render(
31
+ <Component />
32
+ )
33
+
34
+ expect(snapshotOf(element))
35
+ .toMatchSnapshot()
36
+ })
37
+ })
18
38
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-component-snapshot",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Transform a React component to match its snapshot in Jest",
5
5
  "keywords": [
6
6
  "react",
@@ -26,10 +26,12 @@
26
26
  "scripts": {
27
27
  "lint": "eslint",
28
28
  "lint:fix": "npm run lint -- --fix",
29
+ "prepare": "husky",
29
30
  "test": "NODE_ENV=test jest"
30
31
  },
31
32
  "dependencies": {
32
- "debug": "^4.4.0"
33
+ "debug": "^4.4.0",
34
+ "react-component-name": "^0.0.0"
33
35
  },
34
36
  "devDependencies": {
35
37
  "@babel/core": "^7.26.10",
@@ -42,13 +44,13 @@
42
44
  "@testing-library/react": "^16.2.0",
43
45
  "@types/debug": "^4.1.12",
44
46
  "@types/jest": "^29.5.14",
45
- "@types/prop-types": "^15.7.14",
46
47
  "@types/react": "^19.0.11",
47
48
  "@typescript-eslint/parser": "^8.26.1",
48
49
  "core-js": "^3.41.0",
49
50
  "eslint": "^9.22.0",
50
51
  "eslint-plugin-react": "^7.37.4",
51
52
  "globals": "^16.0.0",
53
+ "husky": "^9.1.7",
52
54
  "jest": "^29.7.0",
53
55
  "jest-environment-jsdom": "^29.7.0",
54
56
  "react": "19.0.0"
package/src/index.mjs CHANGED
@@ -1,12 +1,23 @@
1
1
  /**
2
+ * @typedef {import('@testing-library/react').RenderResult} RenderType
2
3
  * @typedef {ReactComponentSnapshotTypes.FiberNode} FiberNode
3
4
  */
4
5
 
5
6
  import {
6
- getType
7
- } from './react.mjs'
7
+ getComponentNameFromFiber as getType
8
+ } from 'react-component-name'
8
9
 
9
- const REACT_COMPONENT_SNAPSHOT = Symbol.for('react.test.json')
10
+ import {
11
+ REACT_COMPONENT_SNAPSHOT
12
+ } from './symbols.mjs'
13
+
14
+ /**
15
+ * @param {unknown} v
16
+ * @returns {v is object}
17
+ */
18
+ function isObject (v) {
19
+ return (v || false) instanceof Object && !Array.isArray(v)
20
+ }
10
21
 
11
22
  /**
12
23
  * @param {PropertyKey} key
@@ -24,14 +35,6 @@ export function isReactPropsKey (key) {
24
35
  return String(key).startsWith('__reactProps$')
25
36
  }
26
37
 
27
- /**
28
- * @param {unknown} v
29
- * @returns {v is object}
30
- */
31
- function isObject (v) {
32
- return (v || false) instanceof Object && !Array.isArray(v)
33
- }
34
-
35
38
  /**
36
39
  * @param {Element | Text | React.Component<any, any, any>} stateNode
37
40
  * @returns {string | undefined}
@@ -91,15 +94,105 @@ export function getStateNodeProps (stateNode) {
91
94
  }
92
95
 
93
96
  /**
94
- * @param {{ container: { firstElementChild: Element | null } }} component
97
+ * @param {RenderType} render
95
98
  * @returns {Element | null}
96
99
  */
97
100
  export function getComponentElement ({
98
101
  container: {
99
- firstElementChild: element
102
+ firstElementChild: componentElement = null
100
103
  }
101
104
  }) {
102
- return element
105
+ return componentElement
106
+ }
107
+
108
+ /**
109
+ * @param {RenderType} render
110
+ * @returns {Element | null}
111
+ */
112
+ export function getComponentElementFromRender (render) {
113
+ return (
114
+ getComponentElementFromContainerElement(
115
+ getContainerElementFromRender(render)
116
+ )
117
+ )
118
+ }
119
+
120
+ /**
121
+ * @param {RenderType} render
122
+ * @returns {Element}
123
+ */
124
+ export function getContainerElementFromRender ({
125
+ container: containerElement
126
+ }) {
127
+ return containerElement
128
+ }
129
+
130
+ /**
131
+ * @param {Element} containerElement
132
+ * @returns {Element | null}
133
+ */
134
+ function getComponentElementFromContainerElement ({
135
+ firstElementChild: componentElement = null
136
+ }) {
137
+ return componentElement
138
+ }
139
+
140
+ /**
141
+ * @param {RenderType} render
142
+ * @returns {Record<PropertyKey, unknown> | null}
143
+ */
144
+ export function toSnapshotFromRender (render) {
145
+ const element = getComponentElementFromRender(render)
146
+
147
+ if (element instanceof HTMLElement) {
148
+ const fiberNode = getFiber(element)
149
+
150
+ if (fiberNode) {
151
+ return (
152
+ transform(fiberNode)
153
+ )
154
+ }
155
+ }
156
+
157
+ return null
158
+ }
159
+
160
+ /**
161
+ * @param {Element} containerElement
162
+ * @returns {Record<PropertyKey, unknown> | null}
163
+ */
164
+ export function toSnapshotFromContainerElement (containerElement) {
165
+ const element = getComponentElementFromContainerElement(containerElement)
166
+
167
+ if (element instanceof HTMLElement) {
168
+ const fiberNode = getFiber(element)
169
+
170
+ if (fiberNode) {
171
+ return (
172
+ transform(fiberNode)
173
+ )
174
+ }
175
+ }
176
+
177
+ return null
178
+ }
179
+
180
+ /**
181
+ * @param {Element | null} componentElement
182
+ * @returns {Record<PropertyKey, unknown> | null}
183
+ */
184
+ export function toSnapshotFromComponentElement (componentElement) {
185
+ if (componentElement instanceof HTMLElement) {
186
+ const fiberNode = getFiber(componentElement)
187
+
188
+ if (fiberNode) {
189
+ return (
190
+ transform(fiberNode)
191
+ )
192
+ }
193
+ }
194
+
195
+ return null
103
196
  }
104
197
 
105
198
  /**
@@ -0,0 +1 @@
1
+ export const REACT_COMPONENT_SNAPSHOT = Symbol.for('react.test.json')
@@ -1,28 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- export const REACT_LEGACY_ELEMENT_TYPE = Symbol.for('react.element')
9
- export const REACT_ELEMENT_TYPE = Symbol.for('react.transitional.element')
10
- export const REACT_PORTAL_TYPE = Symbol.for('react.portal')
11
- export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
12
- export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode')
13
- export const REACT_PROFILER_TYPE = Symbol.for('react.profiler')
14
- export const REACT_PROVIDER_TYPE = Symbol.for('react.provider')
15
- export const REACT_CONSUMER_TYPE = Symbol.for('react.consumer')
16
- export const REACT_CONTEXT_TYPE = Symbol.for('react.context')
17
- export const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref')
18
- export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense')
19
- export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list')
20
- export const REACT_MEMO_TYPE = Symbol.for('react.memo')
21
- export const REACT_LAZY_TYPE = Symbol.for('react.lazy')
22
- export const REACT_SCOPE_TYPE = Symbol.for('react.scope')
23
- export const REACT_ACTIVITY_TYPE = Symbol.for('react.activity')
24
- export const REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden')
25
- export const REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker')
26
- export const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel')
27
- export const REACT_POSTPONE_TYPE = Symbol.for('react.postpone')
28
- export const REACT_VIEW_TRANSITION_TYPE = Symbol.for('react.view_transition')
@@ -1,145 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- import {
9
- REACT_CONTEXT_TYPE,
10
- REACT_CONSUMER_TYPE,
11
- REACT_FORWARD_REF_TYPE,
12
- REACT_FRAGMENT_TYPE,
13
- REACT_PORTAL_TYPE,
14
- REACT_MEMO_TYPE,
15
- REACT_PROFILER_TYPE,
16
- REACT_PROVIDER_TYPE,
17
- REACT_STRICT_MODE_TYPE,
18
- REACT_SUSPENSE_TYPE,
19
- REACT_SUSPENSE_LIST_TYPE,
20
- REACT_LAZY_TYPE,
21
- REACT_TRACING_MARKER_TYPE,
22
- REACT_VIEW_TRANSITION_TYPE,
23
- REACT_ACTIVITY_TYPE
24
- } from './ReactSymbols.mjs'
25
-
26
- /**
27
- * @param {{displayName?: string | null}} outerType
28
- * @param {{displayName?: string | null, name?: string | null}} innerType
29
- * @param {string} wrapperName
30
- * @returns
31
- */
32
- function getWrappedName (
33
- outerType,
34
- innerType,
35
- wrapperName
36
- ) {
37
- const outerDisplayName = outerType.displayName
38
-
39
- if (outerDisplayName) {
40
- return outerDisplayName
41
- }
42
-
43
- const innerDisplayName = innerType.displayName || innerType.name || null
44
-
45
- if (innerDisplayName) {
46
- return `${wrapperName}(${innerDisplayName})`
47
- }
48
-
49
- return wrapperName
50
- }
51
-
52
- /**
53
- * @param {{displayName?: string | null}} type
54
- * @returns {string}
55
- */
56
- function getContextName (type) {
57
- return type.displayName || 'Context'
58
- }
59
-
60
- const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference')
61
-
62
- /**
63
- * @param {unknown} type
64
- * @returns {string | null}
65
- */
66
- export default function getComponentNameFromType (type) {
67
- if (type == null) return null
68
-
69
- if (typeof type === 'function') { // @ts-expect-error
70
- if (type.$$typeof === REACT_CLIENT_REFERENCE) return null
71
- return ( // @ts-expect-error
72
- type.displayName || type.name || null
73
- )
74
- }
75
-
76
- if (typeof type === 'string') return type
77
-
78
- switch (type) {
79
- case REACT_FRAGMENT_TYPE:
80
- return 'Fragment'
81
- case REACT_PROFILER_TYPE:
82
- return 'Profiler'
83
- case REACT_STRICT_MODE_TYPE:
84
- return 'StrictMode'
85
- case REACT_SUSPENSE_TYPE:
86
- return 'Suspense'
87
- case REACT_SUSPENSE_LIST_TYPE:
88
- return 'SuspenseList'
89
- case REACT_ACTIVITY_TYPE:
90
- return 'Activity'
91
- case REACT_VIEW_TRANSITION_TYPE:
92
- return 'ViewTransition'
93
- case REACT_TRACING_MARKER_TYPE:
94
- return 'TracingMarker'
95
- }
96
-
97
- if (typeof type === 'object') { // @ts-expect-error
98
- switch (type.$$typeof) {
99
- case REACT_PORTAL_TYPE:
100
- return 'Portal'
101
-
102
- case REACT_PROVIDER_TYPE: // @ts-expect-error
103
- return getContextName(type._context) + '.Provider'
104
-
105
- case REACT_CONTEXT_TYPE:
106
- return getContextName(type) + '.Context'
107
-
108
- case REACT_CONSUMER_TYPE: // @ts-expect-error
109
- return getContextName(type._context) + '.Consumer'
110
-
111
- case REACT_FORWARD_REF_TYPE: // @ts-expect-error
112
- return getWrappedName(type, type.render, 'ForwardRef')
113
-
114
- case REACT_MEMO_TYPE:
115
- {
116
- // @ts-expect-error
117
- const outerName = type.displayName || null
118
-
119
- if (outerName !== null) {
120
- return outerName
121
- }
122
-
123
- // @ts-expect-error
124
- return getComponentNameFromType(type.type) || 'Memo'
125
- }
126
-
127
- case REACT_LAZY_TYPE:
128
- {
129
- // @ts-expect-error
130
- const payload = type._payload
131
-
132
- // @ts-expect-error
133
- const init = type._init
134
-
135
- try {
136
- return getComponentNameFromType(init(payload))
137
- } catch {
138
- return null
139
- }
140
- }
141
- }
142
- }
143
-
144
- return null
145
- }
package/src/react.mjs DELETED
@@ -1,182 +0,0 @@
1
- // @ts-nocheck
2
-
3
- /**
4
- * @typedef {ReactComponentSnapshotTypes.FiberNode} FiberNode
5
- */
6
-
7
- import debug from 'debug'
8
-
9
- const log = debug('react-component-snapshot')
10
-
11
- export const REACT_PORTAL_TYPE = Symbol.for('react.portal')
12
- export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
13
- export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode')
14
- export const REACT_PROFILER_TYPE = Symbol.for('react.profiler')
15
- export const REACT_CONSUMER_TYPE = Symbol.for('react.consumer')
16
- export const REACT_CONTEXT_TYPE = Symbol.for('react.context')
17
- export const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref')
18
- export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense')
19
- export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list')
20
- export const REACT_MEMO_TYPE = Symbol.for('react.memo')
21
- export const REACT_LAZY_TYPE = Symbol.for('react.lazy')
22
- export const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference')
23
-
24
- /**
25
- * @param {{displayName?: string | null}} outerType
26
- * @param {{displayName?: string | null, name?: string | null}} innerType
27
- * @param {string} refKey
28
- * @returns {string | null}
29
- */
30
- function getNameFromRef (outerType, innerType, refKey) {
31
- const outerName = outerType.displayName || null
32
-
33
- if (outerName) return outerName
34
-
35
- const innerName = innerType.displayName || innerType.name || null
36
-
37
- if (innerName) return `${refKey}(${innerName})`
38
-
39
- return refKey
40
- }
41
-
42
- /**
43
- * @param {FiberNode['type']} type
44
- * @returns {string | null}
45
- */
46
- export function getNameFromType (type) {
47
- if (type == null) return null
48
-
49
- if (typeof type === 'function') {
50
- return (
51
- type.$$typeof === REACT_CLIENT_REFERENCE
52
- ? null
53
- : type.displayName || type.name || null
54
- )
55
- }
56
-
57
- if (typeof type === 'string') return type
58
-
59
- switch (type) {
60
- case REACT_FRAGMENT_TYPE:
61
- return 'Fragment'
62
- case REACT_PORTAL_TYPE:
63
- return 'Portal'
64
- case REACT_PROFILER_TYPE:
65
- return 'Profiler'
66
- case REACT_STRICT_MODE_TYPE:
67
- return 'StrictMode'
68
- case REACT_SUSPENSE_TYPE:
69
- return 'Suspense'
70
- case REACT_SUSPENSE_LIST_TYPE:
71
- return 'SuspenseList'
72
- }
73
-
74
- if (typeof type === 'object') {
75
- switch (type.$$typeof) {
76
- case REACT_CONTEXT_TYPE:
77
- return (type.displayName || 'Context') + '.Provider'
78
- case REACT_CONSUMER_TYPE:
79
- return (type._context.displayName || 'Context') + '.Consumer'
80
- case REACT_FORWARD_REF_TYPE:
81
- return getNameFromRef(type, type.render, 'ForwardRef')
82
-
83
- case REACT_MEMO_TYPE:
84
- return (
85
- type.displayName || getNameFromType(type.type) || 'Memo'
86
- )
87
-
88
- case REACT_LAZY_TYPE:
89
- {
90
- const payload = type._payload
91
- const init = type._init
92
-
93
- try {
94
- return getNameFromType(init(payload))
95
- } catch ({ message }) {
96
- log(`Lazy Component error. Message was "${message}"`)
97
- }
98
- }
99
- }
100
- }
101
-
102
- return null
103
- }
104
-
105
- /**
106
- * @param {FiberNode} fiberNode
107
- * @returns {string | null}
108
- */
109
- export function getType (fiberNode) {
110
- const type = fiberNode.type
111
-
112
- switch (fiberNode.tag) {
113
- case 24:
114
- return 'Cache'
115
- case 9:
116
- return (type._context.displayName || 'Context') + '.Consumer'
117
- case 10:
118
- return (type.displayName || 'Context') + '.Provider'
119
- case 18:
120
- return 'DehydratedFragment'
121
- case 11:
122
- return getNameFromRef(type, type.render, 'ForwardRef')
123
- case 7:
124
- return 'Fragment'
125
- case 26:
126
- case 27:
127
- case 5:
128
- return type
129
- case 4:
130
- return 'Portal'
131
- case 3:
132
- return 'Root'
133
- case 6:
134
- return 'Text'
135
- case 16:
136
- return getNameFromType(type)
137
- case 8:
138
- return (
139
- type === REACT_STRICT_MODE_TYPE
140
- ? 'StrictMode'
141
- : 'Mode'
142
- )
143
- case 22:
144
- return 'Offscreen'
145
- case 12:
146
- return 'Profiler'
147
- case 21:
148
- return 'Scope'
149
- case 13:
150
- return 'Suspense'
151
- case 19:
152
- return 'SuspenseList'
153
- case 25:
154
- return 'TracingMarker'
155
- case 1:
156
- case 0:
157
- case 14:
158
- case 15:
159
- if (typeof type === 'function') return type.displayName || type.name || null
160
- if (typeof type === 'string') return type
161
- return null
162
- case 29:
163
- {
164
- const info = fiberNode._debugInfo || null
165
-
166
- if (info != null) {
167
- let i = info.length - 1
168
-
169
- for (i; i >= 0; i--) {
170
- const t = info[i]
171
- const n = t.name
172
-
173
- if (typeof n === 'string') return n
174
- }
175
- }
176
-
177
- if (fiberNode.return !== null) return getType(fiberNode.return)
178
- }
179
- }
180
-
181
- return null
182
- }
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "NodeNext",
4
- "target": "ESNext",
5
- "allowJs": true,
6
- "checkJs": true,
7
- "noEmit": true,
8
- "strict": true,
9
- "jsx": "react"
10
- },
11
- "include": [
12
- "."
13
- ]
14
- }