Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(reactivity): support shallow readonly for collections #3003

Merged
merged 2 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions packages/reactivity/__tests__/collections/shallowReadonly.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { isReactive, isReadonly, shallowReadonly } from '../../src'

describe('reactivity/collections', () => {
describe('shallowReadonly/Map', () => {
;[Map, WeakMap].forEach(Collection => {
test('should make the map/weak-map readonly', () => {
const key = {}
const val = { foo: 1 }
const original = new Collection([[key, val]])
const sroMap = shallowReadonly(original)
expect(isReadonly(sroMap)).toBe(true)
expect(isReactive(sroMap)).toBe(false)
expect(sroMap.get(key)).toBe(val)

sroMap.set(key, {} as any)
expect(
`Set operation on key "[object Object]" failed: target is readonly.`
).toHaveBeenWarned()
})

test('should not make nested values readonly', () => {
const key = {}
const val = { foo: 1 }
const original = new Collection([[key, val]])
const sroMap = shallowReadonly(original)
expect(isReadonly(sroMap.get(key))).toBe(false)
expect(isReactive(sroMap.get(key))).toBe(false)

sroMap.get(key)!.foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})

test('should not make the value generated by the iterable method readonly', () => {
const key = {}
const val = { foo: 1 }
const original = new Map([[key, val]])
const sroMap = shallowReadonly(original)

const values1 = [...sroMap.values()]
const values2 = [...sroMap.entries()]

expect(isReadonly(values1[0])).toBe(false)
expect(isReactive(values1[0])).toBe(false)
expect(values1[0]).toBe(val)

values1[0].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()

expect(isReadonly(values2[0][1])).toBe(false)
expect(isReactive(values2[0][1])).toBe(false)
expect(values2[0][1]).toBe(val)

values2[0][1].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

test('should not make the value generated by the forEach method readonly', () => {
const val = { foo: 1 }
const original = new Map([['key', val]])
const sroMap = shallowReadonly(original)

sroMap.forEach(val => {
expect(isReadonly(val)).toBe(false)
expect(isReactive(val)).toBe(false)
expect(val).toBe(val)

val.foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})
})

describe('shallowReadonly/Set', () => {
test('should make the set/weak-set readonly', () => {
;[Set, WeakSet].forEach(Collection => {
const obj = { foo: 1 }
const original = new Collection([obj])
const sroSet = shallowReadonly(original)
expect(isReadonly(sroSet)).toBe(true)
expect(isReactive(sroSet)).toBe(false)
expect(sroSet.has(obj)).toBe(true)

sroSet.add({} as any)
expect(
`Add operation on key "[object Object]" failed: target is readonly.`
).toHaveBeenWarned()
})
})

test('should not make nested values readonly', () => {
const obj = { foo: 1 }
const original = new Set([obj])
const sroSet = shallowReadonly(original)

const values = [...sroSet.values()]

expect(values[0]).toBe(obj)
expect(isReadonly(values[0])).toBe(false)
expect(isReactive(values[0])).toBe(false)

sroSet.add({} as any)
expect(
`Add operation on key "[object Object]" failed: target is readonly.`
).toHaveBeenWarned()

values[0].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

test('should not make the value generated by the iterable method readonly', () => {
const val = { foo: 1 }
const original = new Set([val])
const sroSet = shallowReadonly(original)

const values1 = [...sroSet.values()]
const values2 = [...sroSet.entries()]

expect(isReadonly(values1[0])).toBe(false)
expect(isReactive(values1[0])).toBe(false)
expect(values1[0]).toBe(val)

values1[0].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()

expect(isReadonly(values2[0][1])).toBe(false)
expect(isReactive(values2[0][1])).toBe(false)
expect(values2[0][1]).toBe(val)

values2[0][1].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

test('should not make the value generated by the forEach method readonly', () => {
const val = { foo: 1 }
const original = new Set([val])
const sroSet = shallowReadonly(original)

sroSet.forEach(val => {
expect(isReadonly(val)).toBe(false)
expect(isReactive(val)).toBe(false)
expect(val).toBe(val)

val.foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})
})
})
29 changes: 0 additions & 29 deletions packages/reactivity/__tests__/readonly.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
markRaw,
effect,
ref,
shallowReadonly,
isProxy,
computed
} from '../src'
Expand Down Expand Up @@ -455,32 +454,4 @@ describe('reactivity/readonly', () => {
'Set operation on key "randomProperty" failed: target is readonly.'
).toHaveBeenWarned()
})

describe('shallowReadonly', () => {
test('should not make non-reactive properties reactive', () => {
const props = shallowReadonly({ n: { foo: 1 } })
expect(isReactive(props.n)).toBe(false)
})

test('should make root level properties readonly', () => {
const props = shallowReadonly({ n: 1 })
// @ts-ignore
props.n = 2
expect(props.n).toBe(1)
expect(
`Set operation on key "n" failed: target is readonly.`
).toHaveBeenWarned()
})

// to retain 2.x behavior.
test('should NOT make nested properties readonly', () => {
const props = shallowReadonly({ n: { foo: 1 } })
// @ts-ignore
props.n.foo = 2
expect(props.n.foo).toBe(2)
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})
})
191 changes: 191 additions & 0 deletions packages/reactivity/__tests__/shallowReadonly.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { isReactive, isReadonly, shallowReadonly } from '../src'

describe('reactivity/shallowReadonly', () => {
test('should not make non-reactive properties reactive', () => {
const props = shallowReadonly({ n: { foo: 1 } })
expect(isReactive(props.n)).toBe(false)
})

test('should make root level properties readonly', () => {
const props = shallowReadonly({ n: 1 })
// @ts-ignore
props.n = 2
expect(props.n).toBe(1)
expect(
`Set operation on key "n" failed: target is readonly.`
).toHaveBeenWarned()
})

// to retain 2.x behavior.
test('should NOT make nested properties readonly', () => {
const props = shallowReadonly({ n: { foo: 1 } })
// @ts-ignore
props.n.foo = 2
expect(props.n.foo).toBe(2)
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

describe('collection/Map', () => {
;[Map, WeakMap].forEach(Collection => {
test('should make the map/weak-map readonly', () => {
const key = {}
const val = { foo: 1 }
const original = new Collection([[key, val]])
const sroMap = shallowReadonly(original)
expect(isReadonly(sroMap)).toBe(true)
expect(isReactive(sroMap)).toBe(false)
expect(sroMap.get(key)).toBe(val)

sroMap.set(key, {} as any)
expect(
`Set operation on key "[object Object]" failed: target is readonly.`
).toHaveBeenWarned()
})

test('should not make nested values readonly', () => {
const key = {}
const val = { foo: 1 }
const original = new Collection([[key, val]])
const sroMap = shallowReadonly(original)
expect(isReadonly(sroMap.get(key))).toBe(false)
expect(isReactive(sroMap.get(key))).toBe(false)

sroMap.get(key)!.foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})

test('should not make the value generated by the iterable method readonly', () => {
const key = {}
const val = { foo: 1 }
const original = new Map([[key, val]])
const sroMap = shallowReadonly(original)

const values1 = [...sroMap.values()]
const values2 = [...sroMap.entries()]

expect(isReadonly(values1[0])).toBe(false)
expect(isReactive(values1[0])).toBe(false)
expect(values1[0]).toBe(val)

values1[0].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()

expect(isReadonly(values2[0][1])).toBe(false)
expect(isReactive(values2[0][1])).toBe(false)
expect(values2[0][1]).toBe(val)

values2[0][1].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

test('should not make the value generated by the forEach method readonly', () => {
const val = { foo: 1 }
const original = new Map([['key', val]])
const sroMap = shallowReadonly(original)

sroMap.forEach(val => {
expect(isReadonly(val)).toBe(false)
expect(isReactive(val)).toBe(false)
expect(val).toBe(val)

val.foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})
})

describe('collection/Set', () => {
test('should make the set/weak-set readonly', () => {
;[Set, WeakSet].forEach(Collection => {
const obj = { foo: 1 }
const original = new Collection([obj])
const sroSet = shallowReadonly(original)
expect(isReadonly(sroSet)).toBe(true)
expect(isReactive(sroSet)).toBe(false)
expect(sroSet.has(obj)).toBe(true)

sroSet.add({} as any)
expect(
`Add operation on key "[object Object]" failed: target is readonly.`
).toHaveBeenWarned()
})
})

test('should not make nested values readonly', () => {
const obj = { foo: 1 }
const original = new Set([obj])
const sroSet = shallowReadonly(original)

const values = [...sroSet.values()]

expect(values[0]).toBe(obj)
expect(isReadonly(values[0])).toBe(false)
expect(isReactive(values[0])).toBe(false)

sroSet.add({} as any)
expect(
`Add operation on key "[object Object]" failed: target is readonly.`
).toHaveBeenWarned()

values[0].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

test('should not make the value generated by the iterable method readonly', () => {
const val = { foo: 1 }
const original = new Set([val])
const sroSet = shallowReadonly(original)

const values1 = [...sroSet.values()]
const values2 = [...sroSet.entries()]

expect(isReadonly(values1[0])).toBe(false)
expect(isReactive(values1[0])).toBe(false)
expect(values1[0]).toBe(val)

values1[0].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()

expect(isReadonly(values2[0][1])).toBe(false)
expect(isReactive(values2[0][1])).toBe(false)
expect(values2[0][1]).toBe(val)

values2[0][1].foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})

test('should not make the value generated by the forEach method readonly', () => {
const val = { foo: 1 }
const original = new Set([val])
const sroSet = shallowReadonly(original)

sroSet.forEach(val => {
expect(isReadonly(val)).toBe(false)
expect(isReactive(val)).toBe(false)
expect(val).toBe(val)

val.foo = 2
expect(
`Set operation on key "foo" failed: target is readonly.`
).not.toHaveBeenWarned()
})
})
})
})
Loading