test: jest warning assert utils
This commit is contained in:
parent
8511237ed9
commit
3efe0ba3cd
@ -1,6 +1,9 @@
|
|||||||
import { reactive, isReactive, toRaw, markNonReactive } from '../src/reactive'
|
import { reactive, isReactive, toRaw, markNonReactive } from '../src/reactive'
|
||||||
|
import { mockWarn } from '@vue/runtime-test'
|
||||||
|
|
||||||
describe('reactivity/reactive', () => {
|
describe('reactivity/reactive', () => {
|
||||||
|
mockWarn()
|
||||||
|
|
||||||
test('Object', () => {
|
test('Object', () => {
|
||||||
const original = { foo: 1 }
|
const original = { foo: 1 }
|
||||||
const observed = reactive(original)
|
const observed = reactive(original)
|
||||||
@ -124,17 +127,11 @@ describe('reactivity/reactive', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('non-observable values', () => {
|
test('non-observable values', () => {
|
||||||
const warn = jest.spyOn(console, 'warn')
|
|
||||||
let lastMsg: string
|
|
||||||
warn.mockImplementation(msg => {
|
|
||||||
lastMsg = msg
|
|
||||||
})
|
|
||||||
|
|
||||||
const getMsg = (value: any) =>
|
|
||||||
`value cannot be made reactive: ${String(value)}`
|
|
||||||
const assertValue = (value: any) => {
|
const assertValue = (value: any) => {
|
||||||
reactive(value)
|
reactive(value)
|
||||||
expect(lastMsg).toMatch(getMsg(value))
|
expect(
|
||||||
|
`value cannot be made reactive: ${String(value)}`
|
||||||
|
).toHaveBeenWarnedLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
// number
|
// number
|
||||||
@ -151,8 +148,6 @@ describe('reactivity/reactive', () => {
|
|||||||
const s = Symbol()
|
const s = Symbol()
|
||||||
assertValue(s)
|
assertValue(s)
|
||||||
|
|
||||||
warn.mockRestore()
|
|
||||||
|
|
||||||
// built-ins should work and return same value
|
// built-ins should work and return same value
|
||||||
const p = Promise.resolve()
|
const p = Promise.resolve()
|
||||||
expect(reactive(p)).toBe(p)
|
expect(reactive(p)).toBe(p)
|
||||||
|
@ -11,18 +11,10 @@ import {
|
|||||||
effect,
|
effect,
|
||||||
ref
|
ref
|
||||||
} from '../src'
|
} from '../src'
|
||||||
|
import { mockWarn } from '@vue/runtime-test'
|
||||||
|
|
||||||
describe('reactivity/readonly', () => {
|
describe('reactivity/readonly', () => {
|
||||||
let warn: any
|
mockWarn()
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
warn = jest.spyOn(console, 'warn')
|
|
||||||
warn.mockImplementation(() => {})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
warn.mockRestore()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Object', () => {
|
describe('Object', () => {
|
||||||
it('should make nested values readonly', () => {
|
it('should make nested values readonly', () => {
|
||||||
@ -49,16 +41,24 @@ describe('reactivity/readonly', () => {
|
|||||||
const observed: any = readonly({ foo: 1, bar: { baz: 2 } })
|
const observed: any = readonly({ foo: 1, bar: { baz: 2 } })
|
||||||
observed.foo = 2
|
observed.foo = 2
|
||||||
expect(observed.foo).toBe(1)
|
expect(observed.foo).toBe(1)
|
||||||
expect(warn).toHaveBeenCalledTimes(1)
|
expect(
|
||||||
|
`Set operation on key "foo" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarnedLast()
|
||||||
observed.bar.baz = 3
|
observed.bar.baz = 3
|
||||||
expect(observed.bar.baz).toBe(2)
|
expect(observed.bar.baz).toBe(2)
|
||||||
expect(warn).toHaveBeenCalledTimes(2)
|
expect(
|
||||||
|
`Set operation on key "baz" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarnedLast()
|
||||||
delete observed.foo
|
delete observed.foo
|
||||||
expect(observed.foo).toBe(1)
|
expect(observed.foo).toBe(1)
|
||||||
expect(warn).toHaveBeenCalledTimes(3)
|
expect(
|
||||||
|
`Delete operation on key "foo" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarnedLast()
|
||||||
delete observed.bar.baz
|
delete observed.bar.baz
|
||||||
expect(observed.bar.baz).toBe(2)
|
expect(observed.bar.baz).toBe(2)
|
||||||
expect(warn).toHaveBeenCalledTimes(4)
|
expect(
|
||||||
|
`Delete operation on key "baz" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarnedLast()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow mutation when unlocked', () => {
|
it('should allow mutation when unlocked', () => {
|
||||||
@ -73,7 +73,7 @@ describe('reactivity/readonly', () => {
|
|||||||
expect(observed.foo).toBeUndefined()
|
expect(observed.foo).toBeUndefined()
|
||||||
expect(observed.bar.qux).toBe(3)
|
expect(observed.bar.qux).toBe(3)
|
||||||
expect('baz' in observed.bar).toBe(false)
|
expect('baz' in observed.bar).toBe(false)
|
||||||
expect(warn).not.toHaveBeenCalled()
|
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not trigger effects when locked', () => {
|
it('should not trigger effects when locked', () => {
|
||||||
@ -128,22 +128,28 @@ describe('reactivity/readonly', () => {
|
|||||||
const observed: any = readonly([{ foo: 1 }])
|
const observed: any = readonly([{ foo: 1 }])
|
||||||
observed[0] = 1
|
observed[0] = 1
|
||||||
expect(observed[0]).not.toBe(1)
|
expect(observed[0]).not.toBe(1)
|
||||||
expect(warn).toHaveBeenCalledTimes(1)
|
expect(
|
||||||
|
`Set operation on key "0" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarned()
|
||||||
observed[0].foo = 2
|
observed[0].foo = 2
|
||||||
expect(observed[0].foo).toBe(1)
|
expect(observed[0].foo).toBe(1)
|
||||||
expect(warn).toHaveBeenCalledTimes(2)
|
expect(
|
||||||
|
`Set operation on key "foo" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
|
||||||
// should block length mutation
|
// should block length mutation
|
||||||
observed.length = 0
|
observed.length = 0
|
||||||
expect(observed.length).toBe(1)
|
expect(observed.length).toBe(1)
|
||||||
expect(observed[0].foo).toBe(1)
|
expect(observed[0].foo).toBe(1)
|
||||||
expect(warn).toHaveBeenCalledTimes(3)
|
expect(
|
||||||
|
`Set operation on key "length" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
|
||||||
// mutation methods invoke set/length internally and thus are blocked as well
|
// mutation methods invoke set/length internally and thus are blocked as well
|
||||||
observed.push(2)
|
observed.push(2)
|
||||||
expect(observed.length).toBe(1)
|
expect(observed.length).toBe(1)
|
||||||
// push triggers two warnings on [1] and .length
|
// push triggers two warnings on [1] and .length
|
||||||
expect(warn).toHaveBeenCalledTimes(5)
|
expect(`target is readonly.`).toHaveBeenWarnedTimes(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow mutation when unlocked', () => {
|
it('should allow mutation when unlocked', () => {
|
||||||
@ -159,7 +165,7 @@ describe('reactivity/readonly', () => {
|
|||||||
expect(observed[2]).toBe(3)
|
expect(observed[2]).toBe(3)
|
||||||
expect(observed[0].foo).toBe(2)
|
expect(observed[0].foo).toBe(2)
|
||||||
expect(observed[0].bar.baz).toBe(3)
|
expect(observed[0].bar.baz).toBe(3)
|
||||||
expect(warn).not.toHaveBeenCalled()
|
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not trigger effects when locked', () => {
|
it('should not trigger effects when locked', () => {
|
||||||
@ -232,7 +238,9 @@ describe('reactivity/readonly', () => {
|
|||||||
map.set(key, 1)
|
map.set(key, 1)
|
||||||
expect(dummy).toBeUndefined()
|
expect(dummy).toBeUndefined()
|
||||||
expect(map.has(key)).toBe(false)
|
expect(map.has(key)).toBe(false)
|
||||||
expect(warn).toHaveBeenCalledTimes(1)
|
expect(
|
||||||
|
`Set operation on key "${key}" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should allow mutation & trigger effect when unlocked', () => {
|
test('should allow mutation & trigger effect when unlocked', () => {
|
||||||
@ -249,7 +257,7 @@ describe('reactivity/readonly', () => {
|
|||||||
lock()
|
lock()
|
||||||
expect(dummy).toBe(isWeak ? 1 : 2)
|
expect(dummy).toBe(isWeak ? 1 : 2)
|
||||||
expect(map.get(key)).toBe(1)
|
expect(map.get(key)).toBe(1)
|
||||||
expect(warn).not.toHaveBeenCalled()
|
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (Collection === Map) {
|
if (Collection === Map) {
|
||||||
@ -301,7 +309,9 @@ describe('reactivity/readonly', () => {
|
|||||||
set.add(key)
|
set.add(key)
|
||||||
expect(dummy).toBe(false)
|
expect(dummy).toBe(false)
|
||||||
expect(set.has(key)).toBe(false)
|
expect(set.has(key)).toBe(false)
|
||||||
expect(warn).toHaveBeenCalledTimes(1)
|
expect(
|
||||||
|
`Add operation on key "${key}" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should allow mutation & trigger effect when unlocked', () => {
|
test('should allow mutation & trigger effect when unlocked', () => {
|
||||||
@ -317,7 +327,7 @@ describe('reactivity/readonly', () => {
|
|||||||
lock()
|
lock()
|
||||||
expect(dummy).toBe(true)
|
expect(dummy).toBe(true)
|
||||||
expect(set.has(key)).toBe(true)
|
expect(set.has(key)).toBe(true)
|
||||||
expect(warn).not.toHaveBeenCalled()
|
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (Collection === Set) {
|
if (Collection === Set) {
|
||||||
@ -396,6 +406,8 @@ describe('reactivity/readonly', () => {
|
|||||||
const n: any = readonly(ref(1))
|
const n: any = readonly(ref(1))
|
||||||
n.value = 2
|
n.value = 2
|
||||||
expect(n.value).toBe(1)
|
expect(n.value).toBe(1)
|
||||||
expect(warn).toHaveBeenCalledTimes(1)
|
expect(
|
||||||
|
`Set operation on key "value" failed: target is readonly.`
|
||||||
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -2,7 +2,7 @@ import { toRaw, reactive, readonly } from './reactive'
|
|||||||
import { track, trigger } from './effect'
|
import { track, trigger } from './effect'
|
||||||
import { OperationTypes } from './operations'
|
import { OperationTypes } from './operations'
|
||||||
import { LOCKED } from './lock'
|
import { LOCKED } from './lock'
|
||||||
import { isObject } from '@vue/shared'
|
import { isObject, capitalize } from '@vue/shared'
|
||||||
|
|
||||||
const toReactive = (value: any) => (isObject(value) ? reactive(value) : value)
|
const toReactive = (value: any) => (isObject(value) ? reactive(value) : value)
|
||||||
const toReadonly = (value: any) => (isObject(value) ? readonly(value) : value)
|
const toReadonly = (value: any) => (isObject(value) ? readonly(value) : value)
|
||||||
@ -166,9 +166,9 @@ function createReadonlyMethod(
|
|||||||
return function(...args: any[]) {
|
return function(...args: any[]) {
|
||||||
if (LOCKED) {
|
if (LOCKED) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
const key = args[0] ? `on key "${args[0]}"` : ``
|
const key = args[0] ? `on key "${args[0]}" ` : ``
|
||||||
console.warn(
|
console.warn(
|
||||||
`${type} operation ${key}failed: target is readonly.`,
|
`${capitalize(type)} operation ${key}failed: target is readonly.`,
|
||||||
toRaw(this)
|
toRaw(this)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,5 @@ export const render = createRenderer({
|
|||||||
export { serialize } from './serialize'
|
export { serialize } from './serialize'
|
||||||
export { triggerEvent } from './triggerEvent'
|
export { triggerEvent } from './triggerEvent'
|
||||||
export * from './nodeOps'
|
export * from './nodeOps'
|
||||||
|
export * from './jestUtils'
|
||||||
export * from '@vue/runtime-core'
|
export * from '@vue/runtime-core'
|
||||||
|
83
packages/runtime-test/src/jestUtils.ts
Normal file
83
packages/runtime-test/src/jestUtils.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
declare global {
|
||||||
|
namespace jest {
|
||||||
|
interface Matchers<R> {
|
||||||
|
toHaveBeenWarned(): R
|
||||||
|
toHaveBeenWarnedLast(): R
|
||||||
|
toHaveBeenWarnedTimes(n: number): R
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockWarn() {
|
||||||
|
expect.extend({
|
||||||
|
toHaveBeenWarned(received: string) {
|
||||||
|
const passed = warn.mock.calls.some(
|
||||||
|
args => args[0].indexOf(received) > -1
|
||||||
|
)
|
||||||
|
if (passed) {
|
||||||
|
return {
|
||||||
|
pass: true,
|
||||||
|
message: () => `expected "${received}" not to have been warned.`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const msgs = warn.mock.calls.map(args => args[0]).join('\n - ')
|
||||||
|
return {
|
||||||
|
pass: false,
|
||||||
|
message: () =>
|
||||||
|
`expected "${received}" to have been warned.\n\nActual messages:\n\n - ${msgs}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toHaveBeenWarnedLast(received: string) {
|
||||||
|
const passed =
|
||||||
|
warn.mock.calls[warn.mock.calls.length - 1][0].indexOf(received) > -1
|
||||||
|
if (passed) {
|
||||||
|
return {
|
||||||
|
pass: true,
|
||||||
|
message: () => `expected "${received}" not to have been warned last.`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const msgs = warn.mock.calls.map(args => args[0]).join('\n - ')
|
||||||
|
return {
|
||||||
|
pass: false,
|
||||||
|
message: () =>
|
||||||
|
`expected "${received}" to have been warned last.\n\nActual messages:\n\n - ${msgs}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toHaveBeenWarnedTimes(received: string, n: number) {
|
||||||
|
let found = 0
|
||||||
|
warn.mock.calls.forEach(args => {
|
||||||
|
if (args[0].indexOf(received) > -1) {
|
||||||
|
found++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (found > 0) {
|
||||||
|
return {
|
||||||
|
pass: true,
|
||||||
|
message: () =>
|
||||||
|
`expected "${received}" not to have been warned ${n} times.`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
pass: false,
|
||||||
|
message: () =>
|
||||||
|
`expected "${received}" to have been warned ${n} times but got ${found}.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let warn: jest.SpyInstance
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
warn = jest.spyOn(console, 'warn')
|
||||||
|
warn.mockImplementation(() => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
warn.mockRestore()
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user