Skip to content

Commit 757af93

Browse files
LittleSoundsxzz
andauthored
test(runtime-vapor): dom/patchProp.ts (#102)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
1 parent bbf5e1d commit 757af93

File tree

2 files changed

+235
-12
lines changed

2 files changed

+235
-12
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import { NOOP } from '@vue/shared'
2+
import {
3+
setDynamicProp as _setDynamicProp,
4+
recordPropMetadata,
5+
setAttr,
6+
setClass,
7+
setDOMProp,
8+
setHtml,
9+
setStyle,
10+
setText,
11+
} from '../../src'
12+
import {
13+
createComponentInstance,
14+
currentInstance,
15+
getCurrentInstance,
16+
setCurrentInstance,
17+
} from '../../src/component'
18+
19+
let removeComponentInstance = NOOP
20+
beforeEach(() => {
21+
const reset = setCurrentInstance(createComponentInstance((() => {}) as any))
22+
removeComponentInstance = () => {
23+
reset()
24+
removeComponentInstance = NOOP
25+
}
26+
})
27+
afterEach(() => {
28+
removeComponentInstance()
29+
})
30+
31+
describe('patchProp', () => {
32+
describe('recordPropMetadata', () => {
33+
test('should record prop metadata', () => {
34+
const node = {} as Node // the node is just a key
35+
let prev = recordPropMetadata(node, 'class', 'foo')
36+
expect(prev).toBeUndefined()
37+
prev = recordPropMetadata(node, 'class', 'bar')
38+
expect(prev).toBe('foo')
39+
prev = recordPropMetadata(node, 'style', 'color: red')
40+
expect(prev).toBeUndefined()
41+
prev = recordPropMetadata(node, 'style', 'color: blue')
42+
expect(prev).toBe('color: red')
43+
44+
expect(getCurrentInstance()?.metadata.get(node)).toEqual({
45+
props: { class: 'bar', style: 'color: blue' },
46+
})
47+
})
48+
49+
test('should have different metadata for different nodes', () => {
50+
const node1 = {} as Node
51+
const node2 = {} as Node
52+
recordPropMetadata(node1, 'class', 'foo')
53+
recordPropMetadata(node2, 'class', 'bar')
54+
expect(getCurrentInstance()?.metadata.get(node1)).toEqual({
55+
props: { class: 'foo' },
56+
})
57+
expect(getCurrentInstance()?.metadata.get(node2)).toEqual({
58+
props: { class: 'bar' },
59+
})
60+
})
61+
62+
test('should not record prop metadata outside of component', () => {
63+
removeComponentInstance()
64+
expect(currentInstance).toBeNull()
65+
66+
// FIXME
67+
expect(() => recordPropMetadata({} as Node, 'class', 'foo')).toThrowError(
68+
'cannot be used out of component',
69+
)
70+
})
71+
})
72+
73+
describe('setClass', () => {
74+
test('should set class', () => {
75+
const el = document.createElement('div')
76+
setClass(el, 'foo')
77+
expect(el.className).toBe('foo')
78+
setClass(el, ['bar', 'baz'])
79+
expect(el.className).toBe('bar baz')
80+
setClass(el, { a: true, b: false })
81+
expect(el.className).toBe('a')
82+
})
83+
})
84+
85+
describe('setStyle', () => {
86+
test('should set style', () => {
87+
const el = document.createElement('div')
88+
setStyle(el, 'color: red')
89+
expect(el.style.cssText).toBe('color: red;')
90+
})
91+
test.fails('shoud set style with object and array property', () => {
92+
const el = document.createElement('div')
93+
setStyle(el, { color: 'red' })
94+
expect(el.style.cssText).toBe('color: red;')
95+
setStyle(el, [{ color: 'blue' }, { fontSize: '12px' }])
96+
expect(el.style.cssText).toBe('color: blue; font-size: 12px;')
97+
})
98+
})
99+
100+
describe('setAttr', () => {
101+
test('should set attribute', () => {
102+
const el = document.createElement('div')
103+
setAttr(el, 'id', 'foo')
104+
expect(el.getAttribute('id')).toBe('foo')
105+
setAttr(el, 'name', 'bar')
106+
expect(el.getAttribute('name')).toBe('bar')
107+
})
108+
109+
test('should remove attribute', () => {
110+
const el = document.createElement('div')
111+
setAttr(el, 'id', 'foo')
112+
setAttr(el, 'data', 'bar')
113+
expect(el.getAttribute('id')).toBe('foo')
114+
expect(el.getAttribute('data')).toBe('bar')
115+
setAttr(el, 'id', null)
116+
expect(el.getAttribute('id')).toBeNull()
117+
setAttr(el, 'data', undefined)
118+
expect(el.getAttribute('data')).toBeNull()
119+
})
120+
121+
test('should set boolean attribute to string', () => {
122+
const el = document.createElement('div')
123+
setAttr(el, 'disabled', true)
124+
expect(el.getAttribute('disabled')).toBe('true')
125+
setAttr(el, 'disabled', false)
126+
expect(el.getAttribute('disabled')).toBe('false')
127+
})
128+
})
129+
130+
describe('setDOMProp', () => {
131+
test('should set DOM property', () => {
132+
const el = document.createElement('div')
133+
setDOMProp(el, 'textContent', 'foo')
134+
expect(el.textContent).toBe('foo')
135+
setDOMProp(el, 'innerHTML', '<p>bar</p>')
136+
expect(el.innerHTML).toBe('<p>bar</p>')
137+
})
138+
})
139+
140+
describe('setDynamicProp', () => {
141+
const element = document.createElement('div')
142+
function setDynamicProp(
143+
key: string,
144+
value: any,
145+
el = element.cloneNode(true) as HTMLElement,
146+
) {
147+
_setDynamicProp(el, key, value)
148+
return el
149+
}
150+
151+
test('should be able to set id', () => {
152+
let res = setDynamicProp('id', 'bar')
153+
expect(res.id).toBe('bar')
154+
})
155+
156+
test('should be able to set class', () => {
157+
let res = setDynamicProp('class', 'foo')
158+
expect(res.className).toBe('foo')
159+
})
160+
161+
test('should be able to set style', () => {
162+
let res = setDynamicProp('style', 'color: red')
163+
expect(res.style.cssText).toBe('color: red;')
164+
})
165+
166+
test('should be able to set .prop', () => {
167+
let res = setDynamicProp('.foo', 'bar')
168+
expect((res as any)['foo']).toBe('bar')
169+
expect(res.getAttribute('foo')).toBeNull()
170+
})
171+
172+
test('should be able to set ^attr', () => {
173+
let res = setDynamicProp('^foo', 'bar')
174+
expect(res.getAttribute('foo')).toBe('bar')
175+
expect((res as any)['foo']).toBeUndefined()
176+
})
177+
178+
test('should be able to set boolean prop', () => {
179+
let res = setDynamicProp(
180+
'disabled',
181+
true,
182+
document.createElement('button'),
183+
)
184+
expect(res.getAttribute('disabled')).toBe('')
185+
setDynamicProp('disabled', false, res)
186+
expect(res.getAttribute('disabled')).toBeNull()
187+
})
188+
189+
// The function shouldSetAsProp has complete tests elsewhere,
190+
// so here we only do a simple test.
191+
test('should be able to set innerHTML and textContent', () => {
192+
let res = setDynamicProp('innerHTML', '<p>bar</p>')
193+
expect(res.innerHTML).toBe('<p>bar</p>')
194+
res = setDynamicProp('textContent', 'foo')
195+
expect(res.textContent).toBe('foo')
196+
})
197+
198+
test.todo('should be able to set something on SVG')
199+
})
200+
201+
describe('setText', () => {
202+
test('should set textContent', () => {
203+
const el = document.createElement('div')
204+
setText(el, 'foo')
205+
expect(el.textContent).toBe('foo')
206+
setText(el, 'bar')
207+
expect(el.textContent).toBe('bar')
208+
})
209+
})
210+
211+
describe('setHtml', () => {
212+
test('should set innerHTML', () => {
213+
const el = document.createElement('div')
214+
setHtml(el, '<p>foo</p>')
215+
expect(el.innerHTML).toBe('<p>foo</p>')
216+
setHtml(el, '<p>bar</p>')
217+
expect(el.innerHTML).toBe('<p>bar</p>')
218+
})
219+
})
220+
})

packages/runtime-vapor/src/dom/patchProp.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ import {
77
} from '@vue/shared'
88
import { currentInstance } from '../component'
99

10+
export function recordPropMetadata(el: Node, key: string, value: any): any {
11+
if (!currentInstance) {
12+
// TODO implement error handling
13+
if (__DEV__) throw new Error('cannot be used out of component')
14+
return
15+
}
16+
let metadata = currentInstance.metadata.get(el)
17+
if (!metadata) {
18+
currentInstance.metadata.set(el, (metadata = { props: {} }))
19+
}
20+
const prev = metadata.props[key]
21+
metadata.props[key] = value
22+
return prev
23+
}
24+
1025
export function setClass(el: Element, value: any) {
1126
const prev = recordPropMetadata(el, 'class', (value = normalizeClass(value)))
1227
if (value !== prev && (value || prev)) {
@@ -65,18 +80,6 @@ export function setDynamicProp(el: Element, key: string, value: any) {
6580
}
6681
}
6782

68-
export function recordPropMetadata(el: Node, key: string, value: any): any {
69-
if (currentInstance) {
70-
let metadata = currentInstance.metadata.get(el)
71-
if (!metadata) {
72-
currentInstance.metadata.set(el, (metadata = { props: {} }))
73-
}
74-
const prev = metadata.props[key]
75-
metadata.props[key] = value
76-
return prev
77-
}
78-
}
79-
8083
export function setText(el: Node, value: any) {
8184
const oldVal = recordPropMetadata(
8285
el,

0 commit comments

Comments
 (0)