diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index ca30dd8232c..fbe274d2c60 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -278,26 +278,54 @@ describe('component props', () => { expect(root.innerHTML).toBe('
2
') }) - test('validator arguments', async () => { - const mockFn = jest.fn((...args: any[]) => true) - const Comp = defineComponent({ - props: { - foo: { - type: Number, - validator: (value, props) => mockFn(value, props) + describe('validator', () => { + test('validator should be called with two arguments', async () => { + const mockFn = jest.fn((...args: any[]) => true) + const Comp = defineComponent({ + props: { + foo: { + type: Number, + validator: (value, props) => mockFn(value, props) + }, + bar: { + type: Number + } }, - bar: { - type: Number - } - }, - template: `
` + template: `
` + }) + + // Note this one is using the main Vue render so it can compile template + // on the fly + const root = document.createElement('div') + domRender(h(Comp, { foo: 1, bar: 2 }), root) + expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 }) }) - // Note this one is using the main Vue render so it can compile template - // on the fly - const root = document.createElement('div') - domRender(h(Comp, { foo: 1, bar: 2 }), root) - expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 }) + test('validator should not be able to mutate other props', async () => { + const mockFn = jest.fn((...args: any[]) => true) + const Comp = defineComponent({ + props: { + foo: { + type: Number, + validator: (value, props) => !!(props.bar = 1) + }, + bar: { + type: Number, + validator: value => mockFn(value) + } + }, + template: `
` + }) + + // Note this one is using the main Vue render so it can compile template + // on the fly + const root = document.createElement('div') + domRender(h(Comp, { foo: 1, bar: 2 }), root) + expect( + `Set operation on key "bar" failed: target is readonly.` + ).toHaveBeenWarnedLast() + expect(mockFn).toHaveBeenCalledWith(2) + }) }) test('warn props mutation', () => { diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index e634314af76..c7568227ae3 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -2,7 +2,8 @@ import { toRaw, shallowReactive, trigger, - TriggerOpTypes + TriggerOpTypes, + shallowReadonly } from '@vue/reactivity' import { EMPTY_OBJ, @@ -575,7 +576,7 @@ function validateProps( key, resolvedValues[key], opt, - resolvedValues + shallowReadonly(resolvedValues), !hasOwn(rawProps, key) && !hasOwn(rawProps, hyphenate(key)) ) }