/
component.test.tsx
309 lines (269 loc) · 10.5 KB
/
component.test.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import { render } from "../../../jest.setup"
import { fireEvent } from "@testing-library/react"
import { createDomMotionComponent, motion } from "../../"
import * as React from "react"
import styled from "styled-components"
describe("motion component rendering and styles", () => {
it("renders", () => {
const { container } = render(<motion.div />)
expect(container.firstChild).toBeTruthy()
})
it("renders motion div component (using createDomMotionComponent) without type errors ", () => {
// onTap is a motion component specific prop
const MotionDiv = createDomMotionComponent("div")
render(
<MotionDiv
id={"myCreatedMotionDiv"}
onTap={() => {
console.log("Just tapping on div")
}}
/>
)
expect(true).toBe(true)
})
it("renders HTML and SVG attributes without type errors", () => {
const Component = () => {
const ref = React.useRef<HTMLButtonElement | null>(null)
return (
<>
<motion.button title="test" type="button" />
<motion.button ref={ref} />
<motion.button
animate={{ rotate: 90 }}
style={{ overflow: "hidden" }}
/>
<motion.img
src="https://framer.com"
alt="alternative tag"
/>
<motion.a href="https://framer.com" />
<motion.div role="progressbar" aria-valuemax={100} />
</>
)
}
const { container } = render(<Component />)
expect(container.firstChild).toBeTruthy()
})
it("hydrates a provided ref by the time useLayoutEffect has fired", () => {
let hasVanillaRef = false
let hasMotionRef = false
const Component = () => {
const vanillaRef = React.useRef<HTMLDivElement>(null)
const motionRef = React.useRef<HTMLDivElement>(null)
React.useLayoutEffect(() => {
if (vanillaRef.current !== null) hasVanillaRef = true
if (motionRef.current !== null) hasMotionRef = true
})
return (
<>
<div ref={vanillaRef} />
<motion.div ref={motionRef} />
</>
)
}
render(<Component />)
expect(hasVanillaRef).toBe(true)
expect(hasMotionRef).toBe(true)
})
it("renders child", () => {
const { getByTestId } = render(
<motion.div>
<div data-testid="child" />
</motion.div>
)
expect(getByTestId("child")).toBeTruthy()
})
it("renders normal event listeners", () => {
const onMouseEnter = jest.fn()
const onMouseLeave = jest.fn()
const { container } = render(
<motion.div
onMouseEnter={() => onMouseEnter()}
onMouseLeave={() => onMouseLeave()}
/>
)
fireEvent.mouseEnter(container.firstChild as Element)
fireEvent.mouseLeave(container.firstChild as Element)
expect(onMouseEnter).toBeCalled()
expect(onMouseLeave).toBeCalled()
})
it("renders custom component", async () => {
const Component = React.forwardRef(
(_props, ref: React.RefObject<HTMLButtonElement>) => (
<button type="submit" disabled ref={ref} />
)
)
const MotionComponent = motion(Component)
const promise = new Promise<Element>((resolve) => {
const { rerender } = render(
<MotionComponent ref={(ref) => resolve(ref as Element)} />
)
rerender(<Component />)
})
return expect(promise).resolves.toHaveAttribute("disabled")
})
it("accepts createref", async () => {
const promise = new Promise<Element>((resolve) => {
const ref = React.createRef<HTMLButtonElement>()
const Component = () => {
React.useEffect(() => {
resolve(ref.current as Element)
})
return <motion.button type="submit" ref={ref} />
}
const { rerender } = render(<Component />)
rerender(<Component />)
})
return expect(promise).resolves.toHaveAttribute("type", "submit")
})
// Note: Some part of the testing chain doesn't support setting transform-origin
it("generates style attribute if passed a special transform style attr", () => {
const { container } = render(
<motion.div style={{ x: 10, background: "#fff" }} />
)
expect(container.firstChild).toHaveStyle(
"transform: translateX(10px) translateZ(0); background: #fff"
)
expect(container.firstChild).toHaveStyle("background: #fff")
})
it("generates style attribute if passed initial", () => {
const { container } = render(
<motion.div initial={{ x: 10, background: "#fff" }} />
)
expect(container.firstChild).toHaveStyle(
"transform: translateX(10px) translateZ(0); background: rgb(255, 255, 255)"
)
})
it("generates style attribute if passed initial as variant label", () => {
const variants = { foo: { x: 10, background: "#fff" } }
const { container } = render(
<motion.div initial="foo" variants={variants} />
)
expect(container.firstChild).toHaveStyle(
"transform: translateX(10px) translateZ(0); background: rgb(255, 255, 255)"
)
})
it("generates style attribute if passed initial as false", () => {
const { container } = render(
<motion.div initial={false} animate={{ x: 100 }} />
)
expect(container.firstChild).toHaveStyle(
"transform: translateX(100px) translateZ(0);"
)
})
// TODO: Replace dynamic variable test when we implement `custom` attribute: https://github.com/framer/company/issues/12508
it("generates style attribute if passed initial as variant label is function", () => {
const variants = { foo: (i: number) => ({ x: i * 10 }) }
const childVariants = { foo: (i: number) => ({ x: i * 10 }) }
const { getByTestId } = render(
<motion.div initial="foo" custom={0} variants={variants}>
<motion.div
variants={childVariants}
data-testid="a"
custom={0}
/>
<motion.div
variants={childVariants}
data-testid="b"
custom={1}
/>
</motion.div>
)
expect(getByTestId("a")).toHaveStyle("transform: none")
expect(getByTestId("b")).toHaveStyle(
"transform: translateX(10px) translateZ(0)"
)
})
it("generates style attribute for children if passed initial as variant label", () => {
const variants = { foo: { x: 10, background: "#fff" } }
const childVariants = { foo: { opacity: 0, color: "#f00" } }
const { getByTestId } = render(
<motion.div initial="foo" variants={variants}>
<motion.div variants={childVariants} data-testid="child" />
</motion.div>
)
expect(getByTestId("child")).toHaveStyle("opacity: 0; color: #f00")
})
it("generates style attribute for nested children if passed initial as variant label", () => {
const variants = { foo: { x: 10, background: "#fff" } }
const childVariants = { foo: { opacity: 0, color: "#f00" } }
const { getByTestId } = render(
<motion.div initial="foo" variants={variants}>
<motion.div variants={childVariants} data-testid="child">
<motion.div
variants={childVariants}
data-testid="nestedchild"
/>
</motion.div>
</motion.div>
)
expect(getByTestId("nestedchild")).toHaveStyle(
"opacity: 0; color: #f00"
)
})
it("doesnt propagate style for children if passed initial as object", () => {
const { getByTestId } = render(
<motion.ul
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
>
<motion.li data-testid="child" />
</motion.ul>
)
expect(getByTestId("child")).not.toHaveStyle(
"opacity: 0; transform: translateY(50px) translateZ(0)"
)
})
it("renders styled component and overwrites style", () => {
const Box = styled.div`
background-color: #fff;
`
const MotionBox = motion(Box)
const { container } = render(
<MotionBox style={{ backgroundColor: "#f00" }} />
)
expect(container.firstChild).toHaveStyle("background-color: #f00")
})
it("renders transform", () => {
const { container } = render(
<motion.div style={{ transform: "translateX(10px)" }} />
)
expect(container.firstChild).toHaveStyle("transform: translateX(10px)")
})
it("filters MotionProps from the DOM", () => {
const { container } = render(<motion.div initial={{ opacity: 0 }} />)
expect(container.firstChild).not.toHaveAttribute("initial")
})
it("it can render inside <StrictMode />", () => {
function Test() {
return <motion.div animate={{ x: 100 }} initial={{ x: 0 }} />
}
const { container, rerender } = render(<Test />)
rerender(<Test />)
expect(container.firstChild).toBeTruthy()
})
it("it can render nested components inside <StrictMode />", () => {
function Test() {
return (
<motion.div
animate="visible"
initial="parent"
variants={{
visible: { y: 0 },
hidden: { y: 5 },
}}
>
<motion.span
initial="child"
variants={{
visible: { y: 0 },
hidden: { y: 5 },
}}
/>
</motion.div>
)
}
const { container } = render(<Test />)
expect(container.firstChild).toBeTruthy()
})
})