@planningcenter/tapestry-migration-cli 3.1.0-rc.7 → 3.1.0-rc.9
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/package.json +3 -3
- package/src/components/input/transforms/removeTypeInput.test.ts +212 -0
- package/src/components/input/transforms/removeTypeInput.ts +22 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +33 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +2 -1
- package/src/components/text-area/index.ts +48 -0
- package/src/components/text-area/transforms/auditSpreadProps.test.ts +139 -0
- package/src/components/text-area/transforms/auditSpreadProps.ts +10 -0
- package/src/components/text-area/transforms/convertStyleProps.test.ts +158 -0
- package/src/components/text-area/transforms/convertStyleProps.ts +10 -0
- package/src/components/text-area/transforms/innerRefToRef.test.ts +206 -0
- package/src/components/text-area/transforms/innerRefToRef.ts +14 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.test.ts +477 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.ts +227 -0
- package/src/components/text-area/transforms/moveTextAreaImport.test.ts +168 -0
- package/src/components/text-area/transforms/moveTextAreaImport.ts +13 -0
- package/src/components/text-area/transforms/removeDuplicateKeys.test.ts +129 -0
- package/src/components/text-area/transforms/removeDuplicateKeys.ts +8 -0
- package/src/components/text-area/transforms/removeRedundantAriaLabel.test.ts +183 -0
- package/src/components/text-area/transforms/removeRedundantAriaLabel.ts +59 -0
- package/src/components/text-area/transforms/sizeMapping.test.ts +199 -0
- package/src/components/text-area/transforms/sizeMapping.ts +15 -0
- package/src/components/text-area/transforms/stateToInvalid.test.ts +204 -0
- package/src/components/text-area/transforms/stateToInvalid.ts +57 -0
- package/src/components/text-area/transforms/stateToInvalidTernary.test.ts +133 -0
- package/src/components/text-area/transforms/stateToInvalidTernary.ts +11 -0
- package/src/components/text-area/transforms/unsupportedProps.test.ts +275 -0
- package/src/components/text-area/transforms/unsupportedProps.ts +35 -0
- package/src/index.ts +3 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./removeRedundantAriaLabel"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
// Helper to run transform and get result
|
|
9
|
+
function applyTransform(source: string): string | null {
|
|
10
|
+
const fileInfo = { path: "test.tsx", source }
|
|
11
|
+
const api = {
|
|
12
|
+
j,
|
|
13
|
+
jscodeshift: j,
|
|
14
|
+
report: () => {},
|
|
15
|
+
stats: () => {},
|
|
16
|
+
}
|
|
17
|
+
const result = transform(fileInfo, api, {})
|
|
18
|
+
return result as string | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("removeRedundantAriaLabel transform", () => {
|
|
22
|
+
describe("basic transformation", () => {
|
|
23
|
+
it("should remove aria-label when both aria-label and label are equal static strings", () => {
|
|
24
|
+
const input = `
|
|
25
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
26
|
+
|
|
27
|
+
function Component() {
|
|
28
|
+
return <TextArea aria-label="Name" label="Name" />
|
|
29
|
+
}
|
|
30
|
+
`
|
|
31
|
+
|
|
32
|
+
const result = applyTransform(input)
|
|
33
|
+
|
|
34
|
+
expect(result).toContain('<TextArea label="Name" />')
|
|
35
|
+
expect(result).not.toContain("aria-label")
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it("should preserve aria-label when no label prop exists", () => {
|
|
39
|
+
const input = `
|
|
40
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
41
|
+
|
|
42
|
+
function Component() {
|
|
43
|
+
return <TextArea aria-label="Name" />
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
const result = applyTransform(input)
|
|
48
|
+
|
|
49
|
+
expect(result).toBeNull()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("should return null when TextArea has neither aria-label nor label", () => {
|
|
53
|
+
const input = `
|
|
54
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
55
|
+
|
|
56
|
+
function Component() {
|
|
57
|
+
return <TextArea onChange={handleChange} />
|
|
58
|
+
}
|
|
59
|
+
`
|
|
60
|
+
|
|
61
|
+
const result = applyTransform(input)
|
|
62
|
+
|
|
63
|
+
expect(result).toBeNull()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe("preserves aria-label when not provably redundant", () => {
|
|
68
|
+
it("should keep aria-label when it differs from label", () => {
|
|
69
|
+
const input = `
|
|
70
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
71
|
+
|
|
72
|
+
function Component() {
|
|
73
|
+
return <TextArea aria-label="Enter your name" label="Name" />
|
|
74
|
+
}
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
const result = applyTransform(input)
|
|
78
|
+
|
|
79
|
+
expect(result).toBeNull()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("should keep aria-label when label is a dynamic expression", () => {
|
|
83
|
+
const input = `
|
|
84
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
85
|
+
|
|
86
|
+
function Component() {
|
|
87
|
+
return <TextArea aria-label="Name" label={labelText} />
|
|
88
|
+
}
|
|
89
|
+
`
|
|
90
|
+
|
|
91
|
+
const result = applyTransform(input)
|
|
92
|
+
|
|
93
|
+
expect(result).toBeNull()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it("should keep aria-label when it is a dynamic expression", () => {
|
|
97
|
+
const input = `
|
|
98
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
99
|
+
|
|
100
|
+
function Component() {
|
|
101
|
+
return <TextArea aria-label={ariaText} label="Name" />
|
|
102
|
+
}
|
|
103
|
+
`
|
|
104
|
+
|
|
105
|
+
const result = applyTransform(input)
|
|
106
|
+
|
|
107
|
+
expect(result).toBeNull()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("should keep aria-label when label is a JSX element", () => {
|
|
111
|
+
const input = `
|
|
112
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
113
|
+
|
|
114
|
+
function Component() {
|
|
115
|
+
return <TextArea aria-label="Name" label={<span>Name</span>} />
|
|
116
|
+
}
|
|
117
|
+
`
|
|
118
|
+
|
|
119
|
+
const result = applyTransform(input)
|
|
120
|
+
|
|
121
|
+
expect(result).toBeNull()
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("edge cases", () => {
|
|
126
|
+
it("should not affect other components with the same props", () => {
|
|
127
|
+
const input = `
|
|
128
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
129
|
+
|
|
130
|
+
function Component() {
|
|
131
|
+
return (
|
|
132
|
+
<div>
|
|
133
|
+
<TextArea aria-label="Name" label="Name" />
|
|
134
|
+
<textarea aria-label="Name" label="Name" />
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
`
|
|
139
|
+
|
|
140
|
+
const result = applyTransform(input)
|
|
141
|
+
|
|
142
|
+
expect(result).toContain('<TextArea label="Name" />')
|
|
143
|
+
expect(result).toContain('<textarea aria-label="Name" label="Name" />')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it("should handle expression syntax aria-label={'Name'} matching label='Name'", () => {
|
|
147
|
+
const input = `
|
|
148
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
149
|
+
|
|
150
|
+
function Component() {
|
|
151
|
+
return <TextArea aria-label={"Name"} label="Name" />
|
|
152
|
+
}
|
|
153
|
+
`
|
|
154
|
+
|
|
155
|
+
const result = applyTransform(input)
|
|
156
|
+
|
|
157
|
+
expect(result).toContain('<TextArea label="Name" />')
|
|
158
|
+
expect(result).not.toContain("aria-label")
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it("should handle multiple TextAreas in one file with mixed cases", () => {
|
|
162
|
+
const input = `
|
|
163
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
164
|
+
|
|
165
|
+
function Component() {
|
|
166
|
+
return (
|
|
167
|
+
<div>
|
|
168
|
+
<TextArea aria-label="Name" label="Name" />
|
|
169
|
+
<TextArea aria-label="Email" />
|
|
170
|
+
<TextArea label="Phone" />
|
|
171
|
+
</div>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
`
|
|
175
|
+
|
|
176
|
+
const result = applyTransform(input)
|
|
177
|
+
|
|
178
|
+
expect(result).toContain('<TextArea label="Name" />')
|
|
179
|
+
expect(result).toContain('aria-label="Email"')
|
|
180
|
+
expect(result).toContain('<TextArea label="Phone" />')
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { JSXAttribute, JSXSpreadAttribute, Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
4
|
+
import { andConditions } from "../../shared/conditions/andConditions"
|
|
5
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
6
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
7
|
+
|
|
8
|
+
function getStaticStringValue(
|
|
9
|
+
attr: JSXAttribute | JSXSpreadAttribute | undefined
|
|
10
|
+
): string | null {
|
|
11
|
+
if (!attr || attr.type !== "JSXAttribute" || !attr.value) return null
|
|
12
|
+
const value = attr.value
|
|
13
|
+
if (value.type === "Literal" || value.type === "StringLiteral") {
|
|
14
|
+
return typeof value.value === "string" ? value.value : null
|
|
15
|
+
}
|
|
16
|
+
// Handle expression containers with string literals, e.g. aria-label={"Name"}
|
|
17
|
+
if (
|
|
18
|
+
value.type === "JSXExpressionContainer" &&
|
|
19
|
+
value.expression &&
|
|
20
|
+
(value.expression.type === "Literal" ||
|
|
21
|
+
value.expression.type === "StringLiteral") &&
|
|
22
|
+
typeof value.expression.value === "string"
|
|
23
|
+
) {
|
|
24
|
+
return value.expression.value
|
|
25
|
+
}
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const transform: Transform = attributeTransformFactory({
|
|
30
|
+
condition: andConditions(hasAttribute("aria-label"), hasAttribute("label")),
|
|
31
|
+
targetComponent: "TextArea",
|
|
32
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
33
|
+
transform: (element, { j, source }) => {
|
|
34
|
+
const attrs = element.openingElement.attributes || []
|
|
35
|
+
const labelAttr = attrs.find(
|
|
36
|
+
(a) => a.type === "JSXAttribute" && a.name.name === "label"
|
|
37
|
+
)
|
|
38
|
+
const ariaLabelAttr = attrs.find(
|
|
39
|
+
(a) => a.type === "JSXAttribute" && a.name.name === "aria-label"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const labelValue = getStaticStringValue(labelAttr)
|
|
43
|
+
const ariaLabelValue = getStaticStringValue(ariaLabelAttr)
|
|
44
|
+
|
|
45
|
+
// Only remove aria-label when both are static strings and equal
|
|
46
|
+
if (
|
|
47
|
+
labelValue != null &&
|
|
48
|
+
ariaLabelValue != null &&
|
|
49
|
+
labelValue === ariaLabelValue
|
|
50
|
+
) {
|
|
51
|
+
return removeAttribute("aria-label", { element, j, source })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Otherwise keep aria-label to avoid accessibility regressions
|
|
55
|
+
return false
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export default transform
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./sizeMapping"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
const result = transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
return result || source
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("sizeMapping transform", () => {
|
|
19
|
+
describe("size value transformations", () => {
|
|
20
|
+
it("should transform xs to md", () => {
|
|
21
|
+
const input = `
|
|
22
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <TextArea size="xs" label="Extra small textarea" />
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).toContain('size="md"')
|
|
31
|
+
expect(result).not.toContain('size="xs"')
|
|
32
|
+
expect(result).toContain('Size "xs" was mapped to "md"')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("should transform sm to md", () => {
|
|
36
|
+
const input = `
|
|
37
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
function Test() {
|
|
40
|
+
return <TextArea size="sm" label="Small textarea" />
|
|
41
|
+
}
|
|
42
|
+
`.trim()
|
|
43
|
+
|
|
44
|
+
const result = applyTransform(input)
|
|
45
|
+
expect(result).toContain('size="md"')
|
|
46
|
+
expect(result).not.toContain('size="sm"')
|
|
47
|
+
expect(result).toContain('Size "sm" was mapped to "md"')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("should transform xl to lg", () => {
|
|
51
|
+
const input = `
|
|
52
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
53
|
+
|
|
54
|
+
function Test() {
|
|
55
|
+
return <TextArea size="xl" label="Extra large textarea" />
|
|
56
|
+
}
|
|
57
|
+
`.trim()
|
|
58
|
+
|
|
59
|
+
const result = applyTransform(input)
|
|
60
|
+
expect(result).toContain('size="lg"')
|
|
61
|
+
expect(result).not.toContain('size="xl"')
|
|
62
|
+
expect(result).toContain('Size "xl" was mapped to "lg"')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should transform size={"xs"} to md', () => {
|
|
66
|
+
const input = `
|
|
67
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return <TextArea size={"xs"} label="Extra small textarea" />
|
|
71
|
+
}
|
|
72
|
+
`.trim()
|
|
73
|
+
|
|
74
|
+
const result = applyTransform(input)
|
|
75
|
+
expect(result).toContain('size="md"')
|
|
76
|
+
expect(result).toContain('Size "xs" was mapped to "md"')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should not transform supported sizes", () => {
|
|
80
|
+
const input = `
|
|
81
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
82
|
+
|
|
83
|
+
function Test() {
|
|
84
|
+
return (
|
|
85
|
+
<div>
|
|
86
|
+
<TextArea size="md" label="Medium textarea" />
|
|
87
|
+
<TextArea size="lg" label="Large textarea" />
|
|
88
|
+
</div>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
`.trim()
|
|
92
|
+
|
|
93
|
+
const result = applyTransform(input)
|
|
94
|
+
expect(result).toContain('size="md"')
|
|
95
|
+
expect(result).toContain('size="lg"')
|
|
96
|
+
expect(result).not.toContain("TODO: tapestry-migration")
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe("edge cases", () => {
|
|
101
|
+
it("should not affect TextArea without size prop", () => {
|
|
102
|
+
const input = `
|
|
103
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
104
|
+
|
|
105
|
+
function Test() {
|
|
106
|
+
return <TextArea label="No size" />
|
|
107
|
+
}
|
|
108
|
+
`.trim()
|
|
109
|
+
|
|
110
|
+
const result = applyTransform(input)
|
|
111
|
+
expect(result).toBe(input)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should not affect other components", () => {
|
|
115
|
+
const input = `
|
|
116
|
+
import { Button, TextArea } from "@planningcenter/tapestry-react"
|
|
117
|
+
|
|
118
|
+
function Test() {
|
|
119
|
+
return (
|
|
120
|
+
<div>
|
|
121
|
+
<Button size="xs">Small button</Button>
|
|
122
|
+
<TextArea size="md" label="Medium textarea" />
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
`.trim()
|
|
127
|
+
|
|
128
|
+
const result = applyTransform(input)
|
|
129
|
+
expect(result).toContain('size="xs"')
|
|
130
|
+
expect(result).toContain('size="md"')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("should handle multiple TextAreas with different sizes", () => {
|
|
134
|
+
const input = `
|
|
135
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
136
|
+
|
|
137
|
+
function Test() {
|
|
138
|
+
return (
|
|
139
|
+
<div>
|
|
140
|
+
<TextArea size="xs" label="Extra small" />
|
|
141
|
+
<TextArea size="sm" label="Small" />
|
|
142
|
+
<TextArea size="md" label="Medium" />
|
|
143
|
+
<TextArea size="xl" label="Extra large" />
|
|
144
|
+
</div>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
`.trim()
|
|
148
|
+
|
|
149
|
+
const result = applyTransform(input)
|
|
150
|
+
expect(result).not.toContain('size="xs"')
|
|
151
|
+
expect(result).not.toContain('size="sm"')
|
|
152
|
+
expect(result).not.toContain('size="xl"')
|
|
153
|
+
// Should have 3 comments (xs->md, sm->md, xl->lg)
|
|
154
|
+
const sizeMappingMatches = result.match(/Size ".*" was mapped to/g)
|
|
155
|
+
expect(sizeMappingMatches).toHaveLength(3)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it("should preserve other props", () => {
|
|
159
|
+
const input = `
|
|
160
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
161
|
+
|
|
162
|
+
function Test() {
|
|
163
|
+
return (
|
|
164
|
+
<TextArea
|
|
165
|
+
size="xs"
|
|
166
|
+
label="Notes"
|
|
167
|
+
rows={5}
|
|
168
|
+
disabled
|
|
169
|
+
onChange={() => {}}
|
|
170
|
+
/>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
`.trim()
|
|
174
|
+
|
|
175
|
+
const result = applyTransform(input)
|
|
176
|
+
expect(result).toContain('size="md"')
|
|
177
|
+
expect(result).toContain('label="Notes"')
|
|
178
|
+
expect(result).toContain("rows={5}")
|
|
179
|
+
expect(result).toContain("disabled")
|
|
180
|
+
expect(result).toContain("onChange={() => {}}")
|
|
181
|
+
expect(result).toContain('Size "xs" was mapped to "md"')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it("should not transform expression values", () => {
|
|
185
|
+
const input = `
|
|
186
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
187
|
+
|
|
188
|
+
function Test() {
|
|
189
|
+
const size = "xs"
|
|
190
|
+
return <TextArea size={size} label="Variable size" />
|
|
191
|
+
}
|
|
192
|
+
`.trim()
|
|
193
|
+
|
|
194
|
+
const result = applyTransform(input)
|
|
195
|
+
expect(result).toContain("size={size}")
|
|
196
|
+
expect(result).not.toContain("Size")
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { sizeMappingFactory } from "../../shared/transformFactories/sizeMappingFactory"
|
|
4
|
+
|
|
5
|
+
const transform: Transform = sizeMappingFactory({
|
|
6
|
+
sizeMapping: {
|
|
7
|
+
sm: "md",
|
|
8
|
+
xl: "lg",
|
|
9
|
+
xs: "md",
|
|
10
|
+
},
|
|
11
|
+
targetComponent: "TextArea",
|
|
12
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export default transform
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./stateToInvalid"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
const result = transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
return result || source
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("stateToInvalid transform", () => {
|
|
19
|
+
describe("state='error' conversion", () => {
|
|
20
|
+
it("should convert state='error' to invalid", () => {
|
|
21
|
+
const input = `
|
|
22
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <TextArea state="error" label="Notes" />
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).toContain("invalid")
|
|
31
|
+
expect(result).not.toContain('state="error"')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should convert state={"error"} expression to invalid', () => {
|
|
35
|
+
const input = `
|
|
36
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
37
|
+
|
|
38
|
+
function Test() {
|
|
39
|
+
return <TextArea state={"error"} label="Notes" />
|
|
40
|
+
}
|
|
41
|
+
`.trim()
|
|
42
|
+
|
|
43
|
+
const result = applyTransform(input)
|
|
44
|
+
expect(result).toContain("invalid")
|
|
45
|
+
expect(result).not.toContain("state=")
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("should preserve other props when converting state='error'", () => {
|
|
49
|
+
const input = `
|
|
50
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
51
|
+
|
|
52
|
+
function Test() {
|
|
53
|
+
return (
|
|
54
|
+
<TextArea
|
|
55
|
+
state="error"
|
|
56
|
+
label="Notes"
|
|
57
|
+
placeholder="Enter notes"
|
|
58
|
+
onChange={handleChange}
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
`.trim()
|
|
63
|
+
|
|
64
|
+
const result = applyTransform(input)
|
|
65
|
+
expect(result).toContain("invalid")
|
|
66
|
+
expect(result).not.toContain("state=")
|
|
67
|
+
expect(result).toContain('label="Notes"')
|
|
68
|
+
expect(result).toContain('placeholder="Enter notes"')
|
|
69
|
+
expect(result).toContain("onChange={handleChange}")
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe("state='success' conversion", () => {
|
|
74
|
+
it("should remove state='success' and add comment", () => {
|
|
75
|
+
const input = `
|
|
76
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
77
|
+
|
|
78
|
+
function Test() {
|
|
79
|
+
return <TextArea state="success" label="Notes" />
|
|
80
|
+
}
|
|
81
|
+
`.trim()
|
|
82
|
+
|
|
83
|
+
const result = applyTransform(input)
|
|
84
|
+
expect(result).not.toContain('<TextArea state="success"')
|
|
85
|
+
expect(result).toContain("TODO: tapestry-migration (state)")
|
|
86
|
+
expect(result).toContain("no equivalent")
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should remove state={"success"} and add comment', () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
function Test() {
|
|
94
|
+
return <TextArea state={"success"} label="Notes" />
|
|
95
|
+
}
|
|
96
|
+
`.trim()
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(input)
|
|
99
|
+
expect(result).not.toContain("state={")
|
|
100
|
+
expect(result).toContain("TODO: tapestry-migration (state)")
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe("dynamic state value", () => {
|
|
105
|
+
it("should add comment for variable state value", () => {
|
|
106
|
+
const input = `
|
|
107
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
108
|
+
|
|
109
|
+
function Test() {
|
|
110
|
+
const inputState = isError ? "error" : undefined
|
|
111
|
+
return <TextArea state={inputState} label="Notes" />
|
|
112
|
+
}
|
|
113
|
+
`.trim()
|
|
114
|
+
|
|
115
|
+
const result = applyTransform(input)
|
|
116
|
+
expect(result).toContain("TODO: tapestry-migration (state)")
|
|
117
|
+
expect(result).toContain("'state' has been replaced by 'invalid'")
|
|
118
|
+
expect(result).toContain("state={inputState}")
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it("should add comment for string variable", () => {
|
|
122
|
+
const input = `
|
|
123
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
124
|
+
|
|
125
|
+
function Test() {
|
|
126
|
+
return <TextArea state={currentState} label="Notes" />
|
|
127
|
+
}
|
|
128
|
+
`.trim()
|
|
129
|
+
|
|
130
|
+
const result = applyTransform(input)
|
|
131
|
+
expect(result).toContain("TODO: tapestry-migration (state)")
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
describe("edge cases", () => {
|
|
136
|
+
it("should not affect TextArea without state prop", () => {
|
|
137
|
+
const input = `
|
|
138
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
139
|
+
|
|
140
|
+
function Test() {
|
|
141
|
+
return <TextArea label="Notes" />
|
|
142
|
+
}
|
|
143
|
+
`.trim()
|
|
144
|
+
|
|
145
|
+
const result = applyTransform(input)
|
|
146
|
+
expect(result).toBe(input)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it("should not affect other components", () => {
|
|
150
|
+
const input = `
|
|
151
|
+
import { TextArea, Button } from "@planningcenter/tapestry-react"
|
|
152
|
+
|
|
153
|
+
function Test() {
|
|
154
|
+
return (
|
|
155
|
+
<div>
|
|
156
|
+
<Button state="error">Error</Button>
|
|
157
|
+
<TextArea state="error" label="Notes" />
|
|
158
|
+
</div>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
`.trim()
|
|
162
|
+
|
|
163
|
+
const result = applyTransform(input)
|
|
164
|
+
expect(result).toContain('<Button state="error">Error</Button>')
|
|
165
|
+
expect(result).not.toContain('<TextArea state="error"')
|
|
166
|
+
expect(result).toContain("invalid")
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it("should not transform if not imported from @planningcenter/tapestry-react", () => {
|
|
170
|
+
const input = `
|
|
171
|
+
import { TextArea } from "other-library"
|
|
172
|
+
|
|
173
|
+
function Test() {
|
|
174
|
+
return <TextArea state="error" label="Notes" />
|
|
175
|
+
}
|
|
176
|
+
`.trim()
|
|
177
|
+
|
|
178
|
+
const result = applyTransform(input)
|
|
179
|
+
expect(result).toBe(input)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it("should handle multiple TextAreas with different states", () => {
|
|
183
|
+
const input = `
|
|
184
|
+
import { TextArea } from "@planningcenter/tapestry-react"
|
|
185
|
+
|
|
186
|
+
function Test() {
|
|
187
|
+
return (
|
|
188
|
+
<div>
|
|
189
|
+
<TextArea state="error" label="Error field" />
|
|
190
|
+
<TextArea state="success" label="Success field" />
|
|
191
|
+
<TextArea label="Normal field" />
|
|
192
|
+
</div>
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
`.trim()
|
|
196
|
+
|
|
197
|
+
const result = applyTransform(input)
|
|
198
|
+
expect(result).toContain("invalid")
|
|
199
|
+
expect(result).toContain("TODO: tapestry-migration (state)")
|
|
200
|
+
expect(result).not.toContain('<TextArea state="error"')
|
|
201
|
+
expect(result).not.toContain('<TextArea state="success"')
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
})
|