-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
/
Copy pathreactiveArray.spec.ts
213 lines (189 loc) · 5.96 KB
/
reactiveArray.spec.ts
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
import { reactive, isReactive, toRaw } from '../src/reactive'
import { ref, isRef } from '../src/ref'
import { effect } from '../src/effect'
describe('reactivity/reactive/Array', () => {
test('should make Array reactive', () => {
const original = [{ foo: 1 }]
const observed = reactive(original)
expect(observed).not.toBe(original)
expect(isReactive(observed)).toBe(true)
expect(isReactive(original)).toBe(false)
expect(isReactive(observed[0])).toBe(true)
// get
expect(observed[0].foo).toBe(1)
// has
expect(0 in observed).toBe(true)
// ownKeys
expect(Object.keys(observed)).toEqual(['0'])
})
test('cloned reactive Array should point to observed values', () => {
const original = [{ foo: 1 }]
const observed = reactive(original)
const clone = observed.slice()
expect(isReactive(clone[0])).toBe(true)
expect(clone[0]).not.toBe(original[0])
expect(clone[0]).toBe(observed[0])
})
test('observed value should proxy mutations to original (Array)', () => {
const original: any[] = [{ foo: 1 }, { bar: 2 }]
const observed = reactive(original)
// set
const value = { baz: 3 }
const reactiveValue = reactive(value)
observed[0] = value
expect(observed[0]).toBe(reactiveValue)
expect(original[0]).toBe(value)
// delete
delete observed[0]
expect(observed[0]).toBeUndefined()
expect(original[0]).toBeUndefined()
// mutating methods
observed.push(value)
expect(observed[2]).toBe(reactiveValue)
expect(original[2]).toBe(value)
})
test('Array identity methods should work with raw values', () => {
const raw = {}
const arr = reactive([{}, {}])
arr.push(raw)
expect(arr.indexOf(raw)).toBe(2)
expect(arr.indexOf(raw, 3)).toBe(-1)
expect(arr.includes(raw)).toBe(true)
expect(arr.includes(raw, 3)).toBe(false)
expect(arr.lastIndexOf(raw)).toBe(2)
expect(arr.lastIndexOf(raw, 1)).toBe(-1)
// should work also for the observed version
const observed = arr[2]
expect(arr.indexOf(observed)).toBe(2)
expect(arr.indexOf(observed, 3)).toBe(-1)
expect(arr.includes(observed)).toBe(true)
expect(arr.includes(observed, 3)).toBe(false)
expect(arr.lastIndexOf(observed)).toBe(2)
expect(arr.lastIndexOf(observed, 1)).toBe(-1)
})
test('Array identity methods should work if raw value contains reactive objects', () => {
const raw = []
const obj = reactive({})
raw.push(obj)
const arr = reactive(raw)
expect(arr.includes(obj)).toBe(true)
})
test('Array identity methods should be reactive', () => {
const obj = {}
const arr = reactive([obj, {}])
let index: number = -1
effect(() => {
index = arr.indexOf(obj)
})
expect(index).toBe(0)
arr.reverse()
expect(index).toBe(1)
})
test('delete on Array should not trigger length dependency', () => {
const arr = reactive([1, 2, 3])
const fn = vi.fn()
effect(() => {
fn(arr.length)
})
expect(fn).toHaveBeenCalledTimes(1)
delete arr[1]
expect(fn).toHaveBeenCalledTimes(1)
})
test('add existing index on Array should not trigger length dependency', () => {
const array = new Array(3)
const observed = reactive(array)
const fn = vi.fn()
effect(() => {
fn(observed.length)
})
expect(fn).toHaveBeenCalledTimes(1)
observed[1] = 1
expect(fn).toHaveBeenCalledTimes(1)
})
test('add non-integer prop on Array should not trigger length dependency', () => {
const array: any[] & { x?: string } = new Array(3)
const observed = reactive(array)
const fn = vi.fn()
effect(() => {
fn(observed.length)
})
expect(fn).toHaveBeenCalledTimes(1)
observed.x = 'x'
expect(fn).toHaveBeenCalledTimes(1)
observed[-1] = 'x'
expect(fn).toHaveBeenCalledTimes(1)
observed[NaN] = 'x'
expect(fn).toHaveBeenCalledTimes(1)
})
// #2427
test('track length on for ... in iteration', () => {
const array = reactive([1])
let length = ''
effect(() => {
length = ''
for (const key in array) {
length += key
}
})
expect(length).toBe('0')
array.push(1)
expect(length).toBe('01')
})
describe('Array methods w/ refs', () => {
let original: any[]
beforeEach(() => {
original = reactive([1, ref(2)])
})
// read + copy
test('read only copy methods', () => {
const raw = original.concat([3, ref(4)])
expect(isRef(raw[1])).toBe(true)
expect(isRef(raw[3])).toBe(true)
})
// read + write
test('read + write mutating methods', () => {
const res = original.copyWithin(0, 1, 2)
const raw = toRaw(res)
expect(isRef(raw[0])).toBe(true)
expect(isRef(raw[1])).toBe(true)
})
test('read + identity', () => {
const ref = original[1]
expect(ref).toBe(toRaw(original)[1])
expect(original.indexOf(ref)).toBe(1)
})
})
describe('Array subclasses', () => {
class SubArray<T> extends Array<T> {
lastPushed: undefined | T
lastSearched: undefined | T
push(item: T) {
this.lastPushed = item
return super.push(item)
}
indexOf(searchElement: T, fromIndex?: number | undefined): number {
this.lastSearched = searchElement
return super.indexOf(searchElement, fromIndex)
}
}
test('calls correct mutation method on Array subclass', () => {
const subArray = new SubArray(4, 5, 6)
const observed = reactive(subArray)
subArray.push(7)
expect(subArray.lastPushed).toBe(7)
observed.push(9)
expect(observed.lastPushed).toBe(9)
})
test('calls correct identity-sensitive method on Array subclass', () => {
const subArray = new SubArray(4, 5, 6)
const observed = reactive(subArray)
let index
index = subArray.indexOf(4)
expect(index).toBe(0)
expect(subArray.lastSearched).toBe(4)
index = observed.indexOf(6)
expect(index).toBe(2)
expect(observed.lastSearched).toBe(6)
})
})
})