import { reactive, isReactive, toRaw, markNonReactive } from '../src/reactive'
+import { mockWarn } from '@vue/runtime-test'
describe('reactivity/reactive', () => {
+ mockWarn()
+
test('Object', () => {
const original = { foo: 1 }
const observed = reactive(original)
})
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) => {
reactive(value)
- expect(lastMsg).toMatch(getMsg(value))
+ expect(
+ `value cannot be made reactive: ${String(value)}`
+ ).toHaveBeenWarnedLast()
}
// number
const s = Symbol()
assertValue(s)
- warn.mockRestore()
-
// built-ins should work and return same value
const p = Promise.resolve()
expect(reactive(p)).toBe(p)
effect,
ref
} from '../src'
+import { mockWarn } from '@vue/runtime-test'
describe('reactivity/readonly', () => {
- let warn: any
-
- beforeEach(() => {
- warn = jest.spyOn(console, 'warn')
- warn.mockImplementation(() => {})
- })
-
- afterEach(() => {
- warn.mockRestore()
- })
+ mockWarn()
describe('Object', () => {
it('should make nested values readonly', () => {
const observed: any = readonly({ foo: 1, bar: { baz: 2 } })
observed.foo = 2
expect(observed.foo).toBe(1)
- expect(warn).toHaveBeenCalledTimes(1)
+ expect(
+ `Set operation on key "foo" failed: target is readonly.`
+ ).toHaveBeenWarnedLast()
observed.bar.baz = 3
expect(observed.bar.baz).toBe(2)
- expect(warn).toHaveBeenCalledTimes(2)
+ expect(
+ `Set operation on key "baz" failed: target is readonly.`
+ ).toHaveBeenWarnedLast()
delete observed.foo
expect(observed.foo).toBe(1)
- expect(warn).toHaveBeenCalledTimes(3)
+ expect(
+ `Delete operation on key "foo" failed: target is readonly.`
+ ).toHaveBeenWarnedLast()
delete observed.bar.baz
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', () => {
expect(observed.foo).toBeUndefined()
expect(observed.bar.qux).toBe(3)
expect('baz' in observed.bar).toBe(false)
- expect(warn).not.toHaveBeenCalled()
+ expect(`target is readonly`).not.toHaveBeenWarned()
})
it('should not trigger effects when locked', () => {
const observed: any = readonly([{ foo: 1 }])
observed[0] = 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
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
observed.length = 0
expect(observed.length).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
observed.push(2)
expect(observed.length).toBe(1)
// push triggers two warnings on [1] and .length
- expect(warn).toHaveBeenCalledTimes(5)
+ expect(`target is readonly.`).toHaveBeenWarnedTimes(5)
})
it('should allow mutation when unlocked', () => {
expect(observed[2]).toBe(3)
expect(observed[0].foo).toBe(2)
expect(observed[0].bar.baz).toBe(3)
- expect(warn).not.toHaveBeenCalled()
+ expect(`target is readonly`).not.toHaveBeenWarned()
})
it('should not trigger effects when locked', () => {
map.set(key, 1)
expect(dummy).toBeUndefined()
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', () => {
lock()
expect(dummy).toBe(isWeak ? 1 : 2)
expect(map.get(key)).toBe(1)
- expect(warn).not.toHaveBeenCalled()
+ expect(`target is readonly`).not.toHaveBeenWarned()
})
if (Collection === Map) {
set.add(key)
expect(dummy).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', () => {
lock()
expect(dummy).toBe(true)
expect(set.has(key)).toBe(true)
- expect(warn).not.toHaveBeenCalled()
+ expect(`target is readonly`).not.toHaveBeenWarned()
})
if (Collection === Set) {
const n: any = readonly(ref(1))
n.value = 2
expect(n.value).toBe(1)
- expect(warn).toHaveBeenCalledTimes(1)
+ expect(
+ `Set operation on key "value" failed: target is readonly.`
+ ).toHaveBeenWarned()
})
})
--- /dev/null
+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()
+ })
+}