@planningcenter/tapestry-migration-cli 3.2.2-rc.9 → 3.2.2
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/shared/helpers/unsupportedPropsHelpers.ts +24 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.test.ts +330 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +156 -15
- package/src/components/shared/transformFactories/ternaryConditionalToPropFactory.ts +17 -12
- package/src/components/time-field/index.ts +48 -0
- package/src/components/time-field/transforms/auditSpreadProps.test.ts +76 -0
- package/src/components/time-field/transforms/auditSpreadProps.ts +10 -0
- package/src/components/time-field/transforms/convertStyleProps.test.ts +43 -0
- package/src/components/time-field/transforms/convertStyleProps.ts +10 -0
- package/src/components/time-field/transforms/flagMinMax.test.ts +103 -0
- package/src/components/time-field/transforms/flagMinMax.ts +31 -0
- package/src/components/time-field/transforms/mergeFieldIntoTimeField.test.ts +106 -0
- package/src/components/time-field/transforms/mergeFieldIntoTimeField.ts +5 -0
- package/src/components/time-field/transforms/moveTimeFieldImport.test.ts +153 -0
- package/src/components/time-field/transforms/moveTimeFieldImport.ts +14 -0
- package/src/components/time-field/transforms/sizeMapping.test.ts +173 -0
- package/src/components/time-field/transforms/sizeMapping.ts +15 -0
- package/src/components/time-field/transforms/stateToInvalid.test.ts +87 -0
- package/src/components/time-field/transforms/stateToInvalid.ts +56 -0
- package/src/components/time-field/transforms/stateToInvalidTernary.test.ts +100 -0
- package/src/components/time-field/transforms/stateToInvalidTernary.ts +11 -0
- package/src/components/time-field/transforms/tupleToTime.test.ts +182 -0
- package/src/components/time-field/transforms/tupleToTime.ts +107 -0
- package/src/components/time-field/transforms/twelveHourClockToHourCycle.test.ts +117 -0
- package/src/components/time-field/transforms/twelveHourClockToHourCycle.ts +65 -0
- package/src/components/time-field/transforms/unsupportedProps.test.ts +160 -0
- package/src/components/time-field/transforms/unsupportedProps.ts +37 -0
- package/src/index.ts +2 -1
|
@@ -0,0 +1,173 @@
|
|
|
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 { TimeField } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <TimeField size="xs" value={time} onChange={setTime} />
|
|
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 { TimeField } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
function Test() {
|
|
40
|
+
return <TimeField size="sm" value={time} onChange={setTime} />
|
|
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 { TimeField } from "@planningcenter/tapestry-react"
|
|
53
|
+
|
|
54
|
+
function Test() {
|
|
55
|
+
return <TimeField size="xl" value={time} onChange={setTime} />
|
|
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={"sm"} expression container', () => {
|
|
66
|
+
const input = `
|
|
67
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return <TimeField size={"sm"} value={time} onChange={setTime} />
|
|
71
|
+
}
|
|
72
|
+
`.trim()
|
|
73
|
+
|
|
74
|
+
const result = applyTransform(input)
|
|
75
|
+
expect(result).toContain('size="md"')
|
|
76
|
+
expect(result).toContain('Size "sm" was mapped to "md"')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should not transform already valid sizes", () => {
|
|
80
|
+
const input = `
|
|
81
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
82
|
+
|
|
83
|
+
function Test() {
|
|
84
|
+
return (
|
|
85
|
+
<div>
|
|
86
|
+
<TimeField size="md" value={time1} onChange={setTime1} />
|
|
87
|
+
<TimeField size="lg" value={time2} onChange={setTime2} />
|
|
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 TimeField without size prop", () => {
|
|
102
|
+
const input = `
|
|
103
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
104
|
+
|
|
105
|
+
function Test() {
|
|
106
|
+
return <TimeField value={time} onChange={setTime} />
|
|
107
|
+
}
|
|
108
|
+
`.trim()
|
|
109
|
+
|
|
110
|
+
const result = applyTransform(input)
|
|
111
|
+
expect(result).toBe(input)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should not transform expression values (variables)", () => {
|
|
115
|
+
const input = `
|
|
116
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
117
|
+
|
|
118
|
+
function Test() {
|
|
119
|
+
const size = "sm"
|
|
120
|
+
return <TimeField size={size} value={time} onChange={setTime} />
|
|
121
|
+
}
|
|
122
|
+
`.trim()
|
|
123
|
+
|
|
124
|
+
const result = applyTransform(input)
|
|
125
|
+
expect(result).toContain("size={size}")
|
|
126
|
+
expect(result).not.toContain("Size")
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("should not affect other components", () => {
|
|
130
|
+
const input = `
|
|
131
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
132
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
133
|
+
|
|
134
|
+
function Test() {
|
|
135
|
+
return (
|
|
136
|
+
<div>
|
|
137
|
+
<Input size="sm" label="Name" />
|
|
138
|
+
<TimeField size="md" value={time} onChange={setTime} />
|
|
139
|
+
</div>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
`.trim()
|
|
143
|
+
|
|
144
|
+
const result = applyTransform(input)
|
|
145
|
+
expect(result).toContain('<Input size="sm"')
|
|
146
|
+
expect(result).toContain('size="md"')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it("should handle multiple TimeFields with different sizes", () => {
|
|
150
|
+
const input = `
|
|
151
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
152
|
+
|
|
153
|
+
function Test() {
|
|
154
|
+
return (
|
|
155
|
+
<div>
|
|
156
|
+
<TimeField size="xs" value={time1} onChange={setTime1} />
|
|
157
|
+
<TimeField size="sm" value={time2} onChange={setTime2} />
|
|
158
|
+
<TimeField size="md" value={time3} onChange={setTime3} />
|
|
159
|
+
<TimeField size="xl" value={time4} onChange={setTime4} />
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
`.trim()
|
|
164
|
+
|
|
165
|
+
const result = applyTransform(input)
|
|
166
|
+
expect(result).not.toContain('size="xs"')
|
|
167
|
+
expect(result).not.toContain('size="sm"')
|
|
168
|
+
expect(result).not.toContain('size="xl"')
|
|
169
|
+
const sizeMappingMatches = result.match(/Size ".*" was mapped to/g)
|
|
170
|
+
expect(sizeMappingMatches).toHaveLength(3)
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})
|
|
@@ -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: "TimeField",
|
|
12
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export default transform
|
|
@@ -0,0 +1,87 @@
|
|
|
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
|
+
it("converts state='error' to invalid", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <TimeField state="error" value={time} onChange={setTime} />
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).toContain("invalid")
|
|
30
|
+
expect(result).not.toContain('state="error"')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("removes state='success' and adds TODO", () => {
|
|
34
|
+
const input = `
|
|
35
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
36
|
+
|
|
37
|
+
function Test() {
|
|
38
|
+
return <TimeField state="success" value={time} onChange={setTime} />
|
|
39
|
+
}
|
|
40
|
+
`.trim()
|
|
41
|
+
|
|
42
|
+
const result = applyTransform(input)
|
|
43
|
+
expect(result).not.toContain('<TimeField state="success"')
|
|
44
|
+
expect(result).toContain("TODO: tapestry-migration")
|
|
45
|
+
expect(result).toContain("no equivalent")
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("adds TODO comment for dynamic state value", () => {
|
|
49
|
+
const input = `
|
|
50
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
51
|
+
|
|
52
|
+
function Test() {
|
|
53
|
+
return <TimeField state={fieldState} value={time} onChange={setTime} />
|
|
54
|
+
}
|
|
55
|
+
`.trim()
|
|
56
|
+
|
|
57
|
+
const result = applyTransform(input)
|
|
58
|
+
expect(result).toContain("TODO: tapestry-migration")
|
|
59
|
+
expect(result).toContain("Migrate this prop manually")
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it("returns null when state prop is absent", () => {
|
|
63
|
+
const input = `
|
|
64
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
65
|
+
|
|
66
|
+
function Test() {
|
|
67
|
+
return <TimeField value={time} onChange={setTime} />
|
|
68
|
+
}
|
|
69
|
+
`.trim()
|
|
70
|
+
|
|
71
|
+
const result = applyTransform(input)
|
|
72
|
+
expect(result).toBe(input)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("does not affect other components", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
78
|
+
|
|
79
|
+
function Test() {
|
|
80
|
+
return <Button state="error">Click</Button>
|
|
81
|
+
}
|
|
82
|
+
`.trim()
|
|
83
|
+
|
|
84
|
+
const result = applyTransform(input)
|
|
85
|
+
expect(result).toBe(input)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addAttribute } from "../../shared/actions/addAttribute"
|
|
4
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
5
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
6
|
+
import { getAttributeValue } from "../../shared/actions/getAttributeValue"
|
|
7
|
+
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
8
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
9
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
10
|
+
|
|
11
|
+
const transform: Transform = attributeTransformFactory({
|
|
12
|
+
condition: hasAttribute("state"),
|
|
13
|
+
targetComponent: "TimeField",
|
|
14
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
15
|
+
transform: (element, { j, source }) => {
|
|
16
|
+
const attr = getAttribute({ element, name: "state" })
|
|
17
|
+
if (!attr) return false
|
|
18
|
+
|
|
19
|
+
const value = getAttributeValue({ attribute: attr, j })
|
|
20
|
+
|
|
21
|
+
if (value === "error") {
|
|
22
|
+
removeAttribute("state", { element, j, source })
|
|
23
|
+
addAttribute({
|
|
24
|
+
booleanAsShorthand: true,
|
|
25
|
+
element,
|
|
26
|
+
j,
|
|
27
|
+
name: "invalid",
|
|
28
|
+
value: true,
|
|
29
|
+
})
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (value === "success") {
|
|
34
|
+
removeAttribute("state", { element, j, source })
|
|
35
|
+
addComment({
|
|
36
|
+
element,
|
|
37
|
+
j,
|
|
38
|
+
scope: "state",
|
|
39
|
+
source,
|
|
40
|
+
text: "'state=\"success\"' has no equivalent in the new TimeField API. Remove this prop and implement success feedback separately.",
|
|
41
|
+
})
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
addComment({
|
|
46
|
+
element,
|
|
47
|
+
j,
|
|
48
|
+
scope: "state",
|
|
49
|
+
source,
|
|
50
|
+
text: "'state' has been replaced by 'invalid' (boolean). For error state, use invalid={true}. 'success' has no equivalent. Migrate this prop manually.",
|
|
51
|
+
})
|
|
52
|
+
return true
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
export default transform
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./stateToInvalidTernary"
|
|
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("stateToInvalidTernary transform", () => {
|
|
19
|
+
it("converts state={x ? 'error' : undefined} to invalid={x}", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <TimeField state={hasError ? "error" : undefined} value={time} onChange={setTime} />
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).toContain("invalid={hasError}")
|
|
30
|
+
expect(result).not.toContain("state=")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("converts state={x ? 'error' : null} to invalid={x}", () => {
|
|
34
|
+
const input = `
|
|
35
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
36
|
+
|
|
37
|
+
function Test() {
|
|
38
|
+
return <TimeField state={hasError ? "error" : null} value={time} onChange={setTime} />
|
|
39
|
+
}
|
|
40
|
+
`.trim()
|
|
41
|
+
|
|
42
|
+
const result = applyTransform(input)
|
|
43
|
+
expect(result).toContain("invalid={hasError}")
|
|
44
|
+
expect(result).not.toContain("state=")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("converts state={x ? undefined : 'error'} to invalid={!x}", () => {
|
|
48
|
+
const input = `
|
|
49
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
50
|
+
|
|
51
|
+
function Test() {
|
|
52
|
+
return <TimeField state={validateServiceTime(serviceTime) ? undefined : "error"} value={time} onChange={setTime} />
|
|
53
|
+
}
|
|
54
|
+
`.trim()
|
|
55
|
+
|
|
56
|
+
const result = applyTransform(input)
|
|
57
|
+
expect(result).toContain("invalid={!validateServiceTime(serviceTime)}")
|
|
58
|
+
expect(result).not.toContain("state=")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("converts state={x ? null : 'error'} to invalid={!x}", () => {
|
|
62
|
+
const input = `
|
|
63
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
64
|
+
|
|
65
|
+
function Test() {
|
|
66
|
+
return <TimeField state={isValid ? null : "error"} value={time} onChange={setTime} />
|
|
67
|
+
}
|
|
68
|
+
`.trim()
|
|
69
|
+
|
|
70
|
+
const result = applyTransform(input)
|
|
71
|
+
expect(result).toContain("invalid={!isValid}")
|
|
72
|
+
expect(result).not.toContain("state=")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("returns input unchanged when state prop is absent", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
78
|
+
|
|
79
|
+
function Test() {
|
|
80
|
+
return <TimeField value={time} onChange={setTime} />
|
|
81
|
+
}
|
|
82
|
+
`.trim()
|
|
83
|
+
|
|
84
|
+
const result = applyTransform(input)
|
|
85
|
+
expect(result).toBe(input)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it("does not affect other components", () => {
|
|
89
|
+
const input = `
|
|
90
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
91
|
+
|
|
92
|
+
function Test() {
|
|
93
|
+
return <Button state={hasError ? "error" : undefined}>Click</Button>
|
|
94
|
+
}
|
|
95
|
+
`.trim()
|
|
96
|
+
|
|
97
|
+
const result = applyTransform(input)
|
|
98
|
+
expect(result).toBe(input)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
2
|
+
import { ternaryConditionalToPropFactory } from "../../shared/transformFactories/ternaryConditionalToPropFactory"
|
|
3
|
+
|
|
4
|
+
export default ternaryConditionalToPropFactory({
|
|
5
|
+
condition: hasAttribute("state"),
|
|
6
|
+
fromProp: "state",
|
|
7
|
+
matchValue: "error",
|
|
8
|
+
targetComponent: "TimeField",
|
|
9
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
10
|
+
toProp: "invalid",
|
|
11
|
+
})
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./tupleToTime"
|
|
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("tupleToTime transform", () => {
|
|
18
|
+
describe("literal tuple conversion", () => {
|
|
19
|
+
it("converts value={[9, 30]} to value={new Time(9, 30)}", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <TimeField value={[9, 30]} onChange={setTime} />
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).not.toBeNull()
|
|
30
|
+
expect(result).toContain("new Time(9, 30)")
|
|
31
|
+
expect(result).not.toContain("[9, 30]")
|
|
32
|
+
expect(result).toContain("CHANGED: tapestry-migration")
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("converts defaultValue={[14, 0]} to new Time", () => {
|
|
36
|
+
const input = `
|
|
37
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
function Test() {
|
|
40
|
+
return <TimeField defaultValue={[14, 0]} onChange={setTime} />
|
|
41
|
+
}
|
|
42
|
+
`.trim()
|
|
43
|
+
|
|
44
|
+
const result = applyTransform(input)
|
|
45
|
+
expect(result).not.toBeNull()
|
|
46
|
+
expect(result).toContain("new Time(14, 0)")
|
|
47
|
+
expect(result).not.toContain("[14, 0]")
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("imports Time from @internationalized/date when converting", () => {
|
|
51
|
+
const input = `
|
|
52
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
53
|
+
|
|
54
|
+
function Test() {
|
|
55
|
+
return <TimeField value={[9, 30]} onChange={setTime} />
|
|
56
|
+
}
|
|
57
|
+
`.trim()
|
|
58
|
+
|
|
59
|
+
const result = applyTransform(input)
|
|
60
|
+
expect(result).not.toBeNull()
|
|
61
|
+
expect(result).toContain('import { Time } from "@internationalized/date"')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it("does not duplicate Time import if already present", () => {
|
|
65
|
+
const input = `
|
|
66
|
+
import { Time } from "@internationalized/date"
|
|
67
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return <TimeField value={[9, 30]} onChange={setTime} />
|
|
71
|
+
}
|
|
72
|
+
`.trim()
|
|
73
|
+
|
|
74
|
+
const result = applyTransform(input)
|
|
75
|
+
expect(result).not.toBeNull()
|
|
76
|
+
const matches = result!.match(/from ["']@internationalized\/date["']/g)
|
|
77
|
+
expect(matches).toHaveLength(1)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it("converts both value and defaultValue tuples in the same element", () => {
|
|
81
|
+
const input = `
|
|
82
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
83
|
+
|
|
84
|
+
function Test() {
|
|
85
|
+
return <TimeField value={[9, 30]} defaultValue={[8, 0]} onChange={setTime} />
|
|
86
|
+
}
|
|
87
|
+
`.trim()
|
|
88
|
+
|
|
89
|
+
const result = applyTransform(input)
|
|
90
|
+
expect(result).not.toBeNull()
|
|
91
|
+
expect(result).toContain("new Time(9, 30)")
|
|
92
|
+
expect(result).toContain("new Time(8, 0)")
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe("non-tuple expressions", () => {
|
|
97
|
+
it("adds TODO comment for variable references", () => {
|
|
98
|
+
const input = `
|
|
99
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
100
|
+
|
|
101
|
+
function Test() {
|
|
102
|
+
return <TimeField value={currentTime} onChange={setTime} />
|
|
103
|
+
}
|
|
104
|
+
`.trim()
|
|
105
|
+
|
|
106
|
+
const result = applyTransform(input)
|
|
107
|
+
expect(result).not.toBeNull()
|
|
108
|
+
expect(result).toContain("TODO: tapestry-migration")
|
|
109
|
+
expect(result).toContain("TimeValue")
|
|
110
|
+
expect(result).toContain("currentTime")
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it("adds TODO for tuples with non-numeric elements", () => {
|
|
114
|
+
const input = `
|
|
115
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
116
|
+
|
|
117
|
+
function Test() {
|
|
118
|
+
return <TimeField value={[hours, minutes]} onChange={setTime} />
|
|
119
|
+
}
|
|
120
|
+
`.trim()
|
|
121
|
+
|
|
122
|
+
const result = applyTransform(input)
|
|
123
|
+
expect(result).not.toBeNull()
|
|
124
|
+
expect(result).toContain("TODO: tapestry-migration")
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it("does not add Time import when only TODO comments are added", () => {
|
|
128
|
+
const input = `
|
|
129
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
130
|
+
|
|
131
|
+
function Test() {
|
|
132
|
+
return <TimeField value={currentTime} onChange={setTime} />
|
|
133
|
+
}
|
|
134
|
+
`.trim()
|
|
135
|
+
|
|
136
|
+
const result = applyTransform(input)
|
|
137
|
+
expect(result).not.toBeNull()
|
|
138
|
+
expect(result).not.toContain('from "@internationalized/date"')
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe("edge cases", () => {
|
|
143
|
+
it("returns null when TimeField has no value/defaultValue", () => {
|
|
144
|
+
const input = `
|
|
145
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
146
|
+
|
|
147
|
+
function Test() {
|
|
148
|
+
return <TimeField onChange={setTime} />
|
|
149
|
+
}
|
|
150
|
+
`.trim()
|
|
151
|
+
|
|
152
|
+
const result = applyTransform(input)
|
|
153
|
+
expect(result).toBeNull()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it("does not affect TimeField from other packages", () => {
|
|
157
|
+
const input = `
|
|
158
|
+
import { TimeField } from "some-other-lib"
|
|
159
|
+
|
|
160
|
+
function Test() {
|
|
161
|
+
return <TimeField value={[9, 30]} onChange={setTime} />
|
|
162
|
+
}
|
|
163
|
+
`.trim()
|
|
164
|
+
|
|
165
|
+
const result = applyTransform(input)
|
|
166
|
+
expect(result).toBeNull()
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it("does not affect other components", () => {
|
|
170
|
+
const input = `
|
|
171
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
172
|
+
|
|
173
|
+
function Test() {
|
|
174
|
+
return <Input value={[9, 30]} label="Name" />
|
|
175
|
+
}
|
|
176
|
+
`.trim()
|
|
177
|
+
|
|
178
|
+
const result = applyTransform(input)
|
|
179
|
+
expect(result).toBeNull()
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
})
|