@planningcenter/tapestry-migration-cli 2.4.0-rc.2 → 2.4.0-rc.21
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 +100 -6
- package/dist/tapestry-react-shim.cjs +1 -1
- package/package.json +2 -2
- package/src/components/button/index.ts +3 -3
- package/src/components/button/transforms/childrenToLabel.test.ts +5 -4
- package/src/components/button/transforms/childrenToLabel.ts +9 -39
- package/src/components/button/transforms/innerRefToRef.test.ts +170 -0
- package/src/components/button/transforms/innerRefToRef.ts +14 -0
- package/src/components/button/transforms/unsupportedProps.ts +8 -31
- package/src/components/link/index.ts +22 -2
- package/src/components/link/transforms/auditSpreadProps.test.ts +351 -0
- package/src/components/link/transforms/auditSpreadProps.ts +24 -0
- package/src/components/link/transforms/childrenToLabel.test.ts +331 -0
- package/src/components/link/transforms/childrenToLabel.ts +54 -0
- package/src/components/link/transforms/convertStyleProps.test.ts +391 -0
- package/src/components/link/transforms/convertStyleProps.ts +10 -0
- package/src/components/link/transforms/innerRefToRef.test.ts +170 -0
- package/src/components/link/transforms/innerRefToRef.ts +14 -0
- package/src/components/link/transforms/moveLinkImport.test.ts +295 -0
- package/src/components/{button/transforms/linkToButton.ts → link/transforms/moveLinkImport.ts} +4 -5
- package/src/components/link/transforms/removeAs.test.ts +192 -0
- package/src/components/link/transforms/removeAs.ts +17 -0
- package/src/components/link/transforms/{inlineToKind.test.ts → removeInlineMember.test.ts} +13 -28
- package/src/components/link/transforms/removeInlineMember.ts +11 -0
- package/src/components/link/transforms/removeInlineProp.test.ts +295 -0
- package/src/components/link/transforms/removeInlineProp.ts +15 -0
- package/src/components/link/transforms/reviewStyles.test.ts +172 -0
- package/src/components/link/transforms/reviewStyles.ts +17 -0
- package/src/components/link/transforms/targetBlankToExternal.test.ts +14 -0
- package/src/components/link/transforms/tooltipToWrapper.test.ts +392 -0
- package/src/components/link/transforms/tooltipToWrapper.ts +35 -0
- package/src/components/link/transforms/unsupportedProps.test.ts +265 -0
- package/src/components/link/transforms/unsupportedProps.ts +58 -0
- package/src/components/shared/conditions/hasAttributeValue.test.ts +22 -1
- package/src/components/shared/conditions/hasAttributeValue.ts +24 -6
- package/src/components/shared/helpers/childrenToLabelHelpers.ts +43 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +35 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +5 -1
- package/src/index.ts +18 -7
- package/src/stubs/textPlugin.ts +45 -0
- package/src/components/button/transforms/linkToButton.test.ts +0 -426
- package/src/components/link/transforms/inlineToKind.ts +0 -51
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./auditSpreadProps"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
const AUDIT_COMMENT =
|
|
9
|
+
"TODO: tapestry-migration (spreadAttribute): Spread props can contain unsupported props, please explore usages and migrate as needed."
|
|
10
|
+
|
|
11
|
+
function applyTransform(source: string) {
|
|
12
|
+
const fileInfo = { path: "test.tsx", source }
|
|
13
|
+
return transform(
|
|
14
|
+
fileInfo,
|
|
15
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
16
|
+
{}
|
|
17
|
+
) as string | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("auditSpreadProps transform", () => {
|
|
21
|
+
describe("basic transformations", () => {
|
|
22
|
+
it("should add comment to Link with single spread prop", () => {
|
|
23
|
+
const input = `
|
|
24
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
25
|
+
|
|
26
|
+
export default function Test() {
|
|
27
|
+
const props = { href: "/test" }
|
|
28
|
+
return <Link {...props}>Go</Link>
|
|
29
|
+
}
|
|
30
|
+
`.trim()
|
|
31
|
+
|
|
32
|
+
const result = applyTransform(input)
|
|
33
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
34
|
+
expect(result).toContain("{...props}")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should add comment to Link with multiple spread props", () => {
|
|
38
|
+
const input = `
|
|
39
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
40
|
+
|
|
41
|
+
export default function Test() {
|
|
42
|
+
const baseProps = { href: "/test" }
|
|
43
|
+
const styleProps = { className: "link" }
|
|
44
|
+
return <Link {...baseProps} {...styleProps}>Go</Link>
|
|
45
|
+
}
|
|
46
|
+
`.trim()
|
|
47
|
+
|
|
48
|
+
const result = applyTransform(input)
|
|
49
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
50
|
+
expect(result).toContain("{...baseProps}")
|
|
51
|
+
expect(result).toContain("{...styleProps}")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("should handle Link with spread props and regular attributes", () => {
|
|
55
|
+
const input = `
|
|
56
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
57
|
+
|
|
58
|
+
export default function Test() {
|
|
59
|
+
const props = { href: "/test" }
|
|
60
|
+
return <Link label="Go" {...props} external />
|
|
61
|
+
}
|
|
62
|
+
`.trim()
|
|
63
|
+
|
|
64
|
+
const result = applyTransform(input)
|
|
65
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
66
|
+
expect(result).toContain('label="Go"')
|
|
67
|
+
expect(result).toContain("{...props}")
|
|
68
|
+
expect(result).toContain("external")
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it("should handle multiple Link components with spread props", () => {
|
|
72
|
+
const input = `
|
|
73
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
74
|
+
|
|
75
|
+
export default function Test() {
|
|
76
|
+
const props1 = { href: "/test1" }
|
|
77
|
+
const props2 = { href: "/test2" }
|
|
78
|
+
return (
|
|
79
|
+
<div>
|
|
80
|
+
<Link {...props1}>Go 1</Link>
|
|
81
|
+
<Link {...props2}>Go 2</Link>
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
`.trim()
|
|
86
|
+
|
|
87
|
+
const result = applyTransform(input)
|
|
88
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
89
|
+
expect(result).toContain("{...props1}")
|
|
90
|
+
expect(result).toContain("{...props2}")
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe("edge cases", () => {
|
|
95
|
+
it("should not transform Link without spread props", () => {
|
|
96
|
+
const input = `
|
|
97
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
98
|
+
|
|
99
|
+
export default function Test() {
|
|
100
|
+
return <Link href="/test">Go</Link>
|
|
101
|
+
}
|
|
102
|
+
`.trim()
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(input)
|
|
105
|
+
expect(result).toBe(null)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it("should not transform if Link is not imported from @planningcenter/tapestry-react", () => {
|
|
109
|
+
const input = `
|
|
110
|
+
import { Link } from "other-library"
|
|
111
|
+
|
|
112
|
+
export default function Test() {
|
|
113
|
+
const props = { href: "/test" }
|
|
114
|
+
return <Link {...props}>Go</Link>
|
|
115
|
+
}
|
|
116
|
+
`.trim()
|
|
117
|
+
|
|
118
|
+
const result = applyTransform(input)
|
|
119
|
+
expect(result).toBe(null)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it("should handle Link with alias import", () => {
|
|
123
|
+
const input = `
|
|
124
|
+
import { Link as TapestryLink } from "@planningcenter/tapestry-react"
|
|
125
|
+
|
|
126
|
+
export default function Test() {
|
|
127
|
+
const props = { href: "/test" }
|
|
128
|
+
return <TapestryLink {...props}>Go</TapestryLink>
|
|
129
|
+
}
|
|
130
|
+
`.trim()
|
|
131
|
+
|
|
132
|
+
const result = applyTransform(input)
|
|
133
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
134
|
+
expect(result).toContain("{...props}")
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it("should handle mixed Link components (with and without spread props)", () => {
|
|
138
|
+
const input = `
|
|
139
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
140
|
+
|
|
141
|
+
export default function Test() {
|
|
142
|
+
const props = { href: "/test" }
|
|
143
|
+
return (
|
|
144
|
+
<div>
|
|
145
|
+
<Link {...props}>Go</Link>
|
|
146
|
+
<Link href="/static">Static</Link>
|
|
147
|
+
<Link href="/dynamic" external>External</Link>
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
`.trim()
|
|
152
|
+
|
|
153
|
+
const result = applyTransform(input)
|
|
154
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
155
|
+
expect(result).toContain("{...props}")
|
|
156
|
+
expect(result).toContain('<Link href="/static">Static</Link>')
|
|
157
|
+
expect(result).toContain('href="/dynamic"')
|
|
158
|
+
expect(result).toContain("external")
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
describe("complex spread prop scenarios", () => {
|
|
163
|
+
it("should handle Link with complex spread expression", () => {
|
|
164
|
+
const input = `
|
|
165
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
166
|
+
|
|
167
|
+
export default function Test() {
|
|
168
|
+
const baseProps = { className: "link" }
|
|
169
|
+
const additionalProps = isExternal ? { external: true } : {}
|
|
170
|
+
return <Link {...baseProps} {...additionalProps} href="/test">Go</Link>
|
|
171
|
+
}
|
|
172
|
+
`.trim()
|
|
173
|
+
|
|
174
|
+
const result = applyTransform(input)
|
|
175
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
176
|
+
expect(result).toContain("{...baseProps}")
|
|
177
|
+
expect(result).toContain("{...additionalProps}")
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it("should handle Link with spread props in different positions", () => {
|
|
181
|
+
const input = `
|
|
182
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
183
|
+
|
|
184
|
+
export default function Test() {
|
|
185
|
+
const props = { className: "link" }
|
|
186
|
+
return (
|
|
187
|
+
<Link
|
|
188
|
+
label="Go"
|
|
189
|
+
{...props}
|
|
190
|
+
href="/test"
|
|
191
|
+
external={false}
|
|
192
|
+
/>
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
`.trim()
|
|
196
|
+
|
|
197
|
+
const result = applyTransform(input)
|
|
198
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
199
|
+
expect(result).toContain('label="Go"')
|
|
200
|
+
expect(result).toContain("{...props}")
|
|
201
|
+
expect(result).toContain('href="/test"')
|
|
202
|
+
expect(result).toContain("external={false}")
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it("should handle spread props with object expressions", () => {
|
|
206
|
+
const input = `
|
|
207
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
208
|
+
|
|
209
|
+
export default function Test() {
|
|
210
|
+
return <Link {...{ href: "/test", className: "link" }}>Go</Link>
|
|
211
|
+
}
|
|
212
|
+
`.trim()
|
|
213
|
+
|
|
214
|
+
const result = applyTransform(input)
|
|
215
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
216
|
+
expect(result).toContain('{...{ href: "/test", className: "link" }}')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it("should handle spread props with function calls", () => {
|
|
220
|
+
const input = `
|
|
221
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
222
|
+
|
|
223
|
+
export default function Test() {
|
|
224
|
+
return <Link {...getLinkProps()} {...getStyleProps()}>Go</Link>
|
|
225
|
+
}
|
|
226
|
+
`.trim()
|
|
227
|
+
|
|
228
|
+
const result = applyTransform(input)
|
|
229
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
230
|
+
expect(result).toContain("{...getLinkProps()}")
|
|
231
|
+
expect(result).toContain("{...getStyleProps()}")
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
describe("self-closing Link components", () => {
|
|
236
|
+
it("should handle self-closing Link with spread props", () => {
|
|
237
|
+
const input = `
|
|
238
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
239
|
+
|
|
240
|
+
export default function Test() {
|
|
241
|
+
const props = { label: "Go", href: "/test" }
|
|
242
|
+
return <Link {...props} />
|
|
243
|
+
}
|
|
244
|
+
`.trim()
|
|
245
|
+
|
|
246
|
+
const result = applyTransform(input)
|
|
247
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
248
|
+
expect(result).toContain("{...props}")
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it("should handle self-closing Link with multiple spread props and attributes", () => {
|
|
252
|
+
const input = `
|
|
253
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
254
|
+
|
|
255
|
+
export default function Test() {
|
|
256
|
+
const baseProps = { href: "/test" }
|
|
257
|
+
const styleProps = { className: "link" }
|
|
258
|
+
return <Link label="Go" {...baseProps} {...styleProps} external />
|
|
259
|
+
}
|
|
260
|
+
`.trim()
|
|
261
|
+
|
|
262
|
+
const result = applyTransform(input)
|
|
263
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
264
|
+
expect(result).toContain('label="Go"')
|
|
265
|
+
expect(result).toContain("{...baseProps}")
|
|
266
|
+
expect(result).toContain("{...styleProps}")
|
|
267
|
+
expect(result).toContain("external")
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
describe("no changes scenarios", () => {
|
|
272
|
+
it("should return null when no Link components have spread props", () => {
|
|
273
|
+
const input = `
|
|
274
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
275
|
+
|
|
276
|
+
export default function Test() {
|
|
277
|
+
return (
|
|
278
|
+
<div>
|
|
279
|
+
<Link href="/test">Go</Link>
|
|
280
|
+
<Link label="Static" href="/static" />
|
|
281
|
+
<Link href="/dynamic" external>External</Link>
|
|
282
|
+
</div>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
`.trim()
|
|
286
|
+
|
|
287
|
+
const result = applyTransform(input)
|
|
288
|
+
expect(result).toBe(null)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it("should return null when no Link imports exist", () => {
|
|
292
|
+
const input = `
|
|
293
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
294
|
+
|
|
295
|
+
export default function Test() {
|
|
296
|
+
const props = { onClick: handleClick }
|
|
297
|
+
return <Button {...props}>Save</Button>
|
|
298
|
+
}
|
|
299
|
+
`.trim()
|
|
300
|
+
|
|
301
|
+
const result = applyTransform(input)
|
|
302
|
+
expect(result).toBe(null)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it("should return null for empty file", () => {
|
|
306
|
+
const result = applyTransform("")
|
|
307
|
+
expect(result).toBe(null)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it("should return null when file has no JSX", () => {
|
|
311
|
+
const input = `
|
|
312
|
+
export function handleClick() {
|
|
313
|
+
console.log("clicked")
|
|
314
|
+
}
|
|
315
|
+
`.trim()
|
|
316
|
+
|
|
317
|
+
const result = applyTransform(input)
|
|
318
|
+
expect(result).toBe(null)
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
describe("different import scenarios", () => {
|
|
323
|
+
it("should handle default import with spread props", () => {
|
|
324
|
+
const input = `
|
|
325
|
+
import Link from "@planningcenter/tapestry-react"
|
|
326
|
+
|
|
327
|
+
export default function Test() {
|
|
328
|
+
const props = { href: "/test" }
|
|
329
|
+
return <Link {...props}>Go</Link>
|
|
330
|
+
}
|
|
331
|
+
`.trim()
|
|
332
|
+
|
|
333
|
+
const result = applyTransform(input)
|
|
334
|
+
expect(result).toBe(null)
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it("should handle namespace import", () => {
|
|
338
|
+
const input = `
|
|
339
|
+
import * as Tapestry from "@planningcenter/tapestry-react"
|
|
340
|
+
|
|
341
|
+
export default function Test() {
|
|
342
|
+
const props = { href: "/test" }
|
|
343
|
+
return <Tapestry.Link {...props}>Go</Tapestry.Link>
|
|
344
|
+
}
|
|
345
|
+
`.trim()
|
|
346
|
+
|
|
347
|
+
const result = applyTransform(input)
|
|
348
|
+
expect(result).toBe(null)
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
4
|
+
import { getSpreadProps } from "../../shared/actions/getSpreadProps"
|
|
5
|
+
import { hasSpreadProps } from "../../shared/actions/hasSpreadProps"
|
|
6
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
7
|
+
|
|
8
|
+
const COMMENT =
|
|
9
|
+
"Spread props can contain unsupported props, please explore usages and migrate as needed."
|
|
10
|
+
|
|
11
|
+
const transform: Transform = attributeTransformFactory({
|
|
12
|
+
condition: hasSpreadProps,
|
|
13
|
+
targetComponent: "Link",
|
|
14
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
15
|
+
transform: (element, { j }) => {
|
|
16
|
+
const spreadProps = getSpreadProps(element)
|
|
17
|
+
spreadProps.forEach((prop) =>
|
|
18
|
+
addCommentToAttribute({ attribute: prop, j, text: COMMENT })
|
|
19
|
+
)
|
|
20
|
+
return spreadProps.length > 0
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export default transform
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./childrenToLabel"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string | null {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("childrenToLabel transform for Link", () => {
|
|
18
|
+
describe("simple text conversion", () => {
|
|
19
|
+
it("should convert simple text children to label prop", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <Link href="/home">Home</Link>
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
30
|
+
expect(result).not.toContain("Home</Link>")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("should handle text with whitespace", () => {
|
|
34
|
+
const input = `
|
|
35
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
36
|
+
|
|
37
|
+
function Test() {
|
|
38
|
+
return <Link href="/about"> About Us </Link>
|
|
39
|
+
}
|
|
40
|
+
`.trim()
|
|
41
|
+
|
|
42
|
+
const result = applyTransform(input)
|
|
43
|
+
expect(result).toContain('<Link href="/about" label="About Us" />')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("should convert string literal expressions", () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
49
|
+
|
|
50
|
+
function Test() {
|
|
51
|
+
return <Link href="/contact">{"Contact"}</Link>
|
|
52
|
+
}
|
|
53
|
+
`.trim()
|
|
54
|
+
|
|
55
|
+
const result = applyTransform(input)
|
|
56
|
+
expect(result).toContain('<Link href="/contact" label="Contact" />')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it("should convert simple template literals without expressions", () => {
|
|
60
|
+
const input = `
|
|
61
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
62
|
+
|
|
63
|
+
function Test() {
|
|
64
|
+
return <Link href="/profile">{\`Profile\`}</Link>
|
|
65
|
+
}
|
|
66
|
+
`.trim()
|
|
67
|
+
|
|
68
|
+
const result = applyTransform(input)
|
|
69
|
+
expect(result).toContain('<Link href="/profile" label="Profile" />')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it("should combine multiple text nodes", () => {
|
|
73
|
+
const input = `
|
|
74
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
75
|
+
|
|
76
|
+
function Test() {
|
|
77
|
+
return <Link href="/help">Get {"Help"} Now</Link>
|
|
78
|
+
}
|
|
79
|
+
`.trim()
|
|
80
|
+
|
|
81
|
+
const result = applyTransform(input)
|
|
82
|
+
expect(result).toContain('<Link href="/help" label="GetHelpNow" />')
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe("complex children scenarios", () => {
|
|
87
|
+
it("should add comment for complex JSX children", () => {
|
|
88
|
+
const input = `
|
|
89
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
90
|
+
import { Icon } from "./Icon"
|
|
91
|
+
|
|
92
|
+
function Test() {
|
|
93
|
+
return (
|
|
94
|
+
<div>
|
|
95
|
+
<Link href="/save">
|
|
96
|
+
<Icon name="save" />
|
|
97
|
+
Save
|
|
98
|
+
</Link>
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
`.trim()
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(input)
|
|
105
|
+
expect(result).toContain(
|
|
106
|
+
"{/* TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
107
|
+
)
|
|
108
|
+
expect(result).toContain(
|
|
109
|
+
"take time to find the right text for the component"
|
|
110
|
+
)
|
|
111
|
+
expect(result).toContain(
|
|
112
|
+
"If icons are used in the component, you can use prefix and suffix to correctly display those icons"
|
|
113
|
+
)
|
|
114
|
+
expect(result).toContain('<Icon name="save" />')
|
|
115
|
+
expect(result).toContain("Save")
|
|
116
|
+
expect(result).toContain("</Link>")
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it("should add comment for expression children", () => {
|
|
120
|
+
const input = `
|
|
121
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
122
|
+
|
|
123
|
+
function Test({ linkText }) {
|
|
124
|
+
return <Link href="/home">{linkText}</Link>
|
|
125
|
+
}
|
|
126
|
+
`.trim()
|
|
127
|
+
|
|
128
|
+
const result = applyTransform(input)
|
|
129
|
+
expect(result).toContain(
|
|
130
|
+
"TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("should add comment for template literals with expressions", () => {
|
|
135
|
+
const input = `
|
|
136
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
137
|
+
|
|
138
|
+
function Test({ name }) {
|
|
139
|
+
return <Link href="/user">{\`View \${name}\`}</Link>
|
|
140
|
+
}
|
|
141
|
+
`.trim()
|
|
142
|
+
|
|
143
|
+
const result = applyTransform(input)
|
|
144
|
+
expect(result).toContain(
|
|
145
|
+
"TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
146
|
+
)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe("existing label prop scenarios", () => {
|
|
151
|
+
it("should add comment when Link has both label and children", () => {
|
|
152
|
+
const input = `
|
|
153
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
154
|
+
|
|
155
|
+
function Test() {
|
|
156
|
+
return (
|
|
157
|
+
<div>
|
|
158
|
+
<Link href="/home" label="Existing Label">
|
|
159
|
+
Child Content
|
|
160
|
+
</Link>
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
`.trim()
|
|
165
|
+
|
|
166
|
+
const result = applyTransform(input)
|
|
167
|
+
expect(result).toContain(
|
|
168
|
+
"{/* TODO: tapestry-migration (label): Link has both label prop and children"
|
|
169
|
+
)
|
|
170
|
+
expect(result).toContain(
|
|
171
|
+
"take time to find the right text for the component."
|
|
172
|
+
)
|
|
173
|
+
expect(result).not.toContain("If icons are used")
|
|
174
|
+
expect(result).toContain('label="Existing Label"')
|
|
175
|
+
expect(result).toContain("Child Content")
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it("should handle Link with label and simple text children", () => {
|
|
179
|
+
const input = `
|
|
180
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
181
|
+
|
|
182
|
+
function Test() {
|
|
183
|
+
return <Link href="/about" label="About">About Us</Link>
|
|
184
|
+
}
|
|
185
|
+
`.trim()
|
|
186
|
+
|
|
187
|
+
const result = applyTransform(input)
|
|
188
|
+
expect(result).toContain(
|
|
189
|
+
"TODO: tapestry-migration (label): Link has both label prop and children"
|
|
190
|
+
)
|
|
191
|
+
expect(result).not.toContain("If icons are used")
|
|
192
|
+
expect(result).toContain('label="About"')
|
|
193
|
+
expect(result).toContain("About Us")
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
describe("edge cases", () => {
|
|
198
|
+
it("should skip Links with no children", () => {
|
|
199
|
+
const input = `
|
|
200
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
201
|
+
|
|
202
|
+
function Test() {
|
|
203
|
+
return <Link href="/home" label="Home" />
|
|
204
|
+
}
|
|
205
|
+
`.trim()
|
|
206
|
+
|
|
207
|
+
const result = applyTransform(input)
|
|
208
|
+
expect(result).toBeNull()
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it("should skip Links with only whitespace children", () => {
|
|
212
|
+
const input = `
|
|
213
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
214
|
+
|
|
215
|
+
function Test() {
|
|
216
|
+
return <Link href="/home"> </Link>
|
|
217
|
+
}
|
|
218
|
+
`.trim()
|
|
219
|
+
|
|
220
|
+
const result = applyTransform(input)
|
|
221
|
+
expect(result).toBeNull()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it("should handle mixed whitespace and text", () => {
|
|
225
|
+
const input = `
|
|
226
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
227
|
+
|
|
228
|
+
function Test() {
|
|
229
|
+
return <Link href="/home">
|
|
230
|
+
Home
|
|
231
|
+
</Link>
|
|
232
|
+
}
|
|
233
|
+
`.trim()
|
|
234
|
+
|
|
235
|
+
const result = applyTransform(input)
|
|
236
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it("should not transform Link from other packages", () => {
|
|
240
|
+
const input = `
|
|
241
|
+
import { Link } from "some-other-package"
|
|
242
|
+
|
|
243
|
+
function Test() {
|
|
244
|
+
return <Link href="/home">Home</Link>
|
|
245
|
+
}
|
|
246
|
+
`.trim()
|
|
247
|
+
|
|
248
|
+
const result = applyTransform(input)
|
|
249
|
+
expect(result).toBeNull()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it("should handle aliased imports", () => {
|
|
253
|
+
const input = `
|
|
254
|
+
import { Link as TapestryLink } from "@planningcenter/tapestry-react"
|
|
255
|
+
|
|
256
|
+
function Test() {
|
|
257
|
+
return <TapestryLink href="/home">Home</TapestryLink>
|
|
258
|
+
}
|
|
259
|
+
`.trim()
|
|
260
|
+
|
|
261
|
+
const result = applyTransform(input)
|
|
262
|
+
expect(result).toContain('<TapestryLink href="/home" label="Home" />')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it("should return null for files without tapestry-react Link", () => {
|
|
266
|
+
const input = `
|
|
267
|
+
function Test() {
|
|
268
|
+
return <a href="/home">Home</a>
|
|
269
|
+
}
|
|
270
|
+
`.trim()
|
|
271
|
+
|
|
272
|
+
const result = applyTransform(input)
|
|
273
|
+
expect(result).toBeNull()
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
describe("multiple links", () => {
|
|
278
|
+
it("should handle multiple links with different scenarios", () => {
|
|
279
|
+
const input = `
|
|
280
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
281
|
+
|
|
282
|
+
function Test() {
|
|
283
|
+
return (
|
|
284
|
+
<div>
|
|
285
|
+
<Link href="/home">Home</Link>
|
|
286
|
+
<Link href="/about" label="About">About Content</Link>
|
|
287
|
+
<Link href="/contact">{"Contact"}</Link>
|
|
288
|
+
</div>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
`.trim()
|
|
292
|
+
|
|
293
|
+
const result = applyTransform(input)
|
|
294
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
295
|
+
expect(result).toContain(
|
|
296
|
+
"{/* TODO: tapestry-migration (label): Link has both label prop and children"
|
|
297
|
+
)
|
|
298
|
+
expect(result).toContain('<Link href="/contact" label="Contact" />')
|
|
299
|
+
})
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
describe("comment formatting", () => {
|
|
303
|
+
it("should properly format JSX comments in JSX context", () => {
|
|
304
|
+
const input = `
|
|
305
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
306
|
+
|
|
307
|
+
function Test() {
|
|
308
|
+
return (
|
|
309
|
+
<div>
|
|
310
|
+
<Link href="/complex">
|
|
311
|
+
<span>Complex</span>
|
|
312
|
+
</Link>
|
|
313
|
+
</div>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
`.trim()
|
|
317
|
+
|
|
318
|
+
const result = applyTransform(input)
|
|
319
|
+
|
|
320
|
+
// Should have proper JSX comment syntax
|
|
321
|
+
expect(result).toMatch(/\{\s*\/\*\s*TODO: tapestry-migration/)
|
|
322
|
+
expect(result).toMatch(/\*\/\s*\}/)
|
|
323
|
+
expect(result).toContain(
|
|
324
|
+
"take time to find the right text for the component"
|
|
325
|
+
)
|
|
326
|
+
expect(result).toContain(
|
|
327
|
+
"If icons are used in the component, you can use prefix and suffix to correctly display those icons"
|
|
328
|
+
)
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
})
|