diff --git a/src/mock-doc/element.ts b/src/mock-doc/element.ts index 81576b18ac4..4d4cf8a38eb 100644 --- a/src/mock-doc/element.ts +++ b/src/mock-doc/element.ts @@ -126,6 +126,12 @@ patchPropAttributes( }, ); +Object.defineProperty(MockButtonElement.prototype, 'form', { + get(this: MockElement) { + return this.hasAttribute('form') ? this.getAttribute('form') : null; + }, +}); + export class MockImageElement extends MockHTMLElement { constructor(ownerDocument: any) { super(ownerDocument, 'img'); diff --git a/src/runtime/vdom/set-accessor.ts b/src/runtime/vdom/set-accessor.ts index 950fad94fe2..c4e0aa6f20c 100644 --- a/src/runtime/vdom/set-accessor.ts +++ b/src/runtime/vdom/set-accessor.ts @@ -136,7 +136,11 @@ export const setAccessor = ( if (memberName === 'list') { isProp = false; } else if (oldValue == null || (elm as any)[memberName] != n) { - (elm as any)[memberName] = n; + if (typeof (elm as any).__lookupSetter__(memberName) === 'function') { + (elm as any)[memberName] = n; + } else { + elm.setAttribute(memberName, n); + } } } else { (elm as any)[memberName] = newValue; diff --git a/src/runtime/vdom/test/set-accessor.spec.ts b/src/runtime/vdom/test/set-accessor.spec.ts index ff99235244b..9f9bb4529f3 100644 --- a/src/runtime/vdom/test/set-accessor.spec.ts +++ b/src/runtime/vdom/test/set-accessor.spec.ts @@ -886,4 +886,16 @@ describe('setAccessor for standard html elements', () => { expect(elm.style.cssText).toEqual('margin: 30px; color: orange;'); }); }); + + it('uses setAttribute if element has not setter', () => { + const elm = document.createElement('button'); + const spy = jest.spyOn(elm, 'setAttribute'); + setAccessor(elm, 'form', undefined, 'some-form', false, 0); + expect(spy.mock.calls).toEqual([['form', 'some-form']]); + + const elm2 = document.createElement('button'); + const spy2 = jest.spyOn(elm2, 'setAttribute'); + setAccessor(elm2, 'textContent', undefined, 'some-content', false, 0); + expect(spy2.mock.calls).toEqual([]); + }); });