angular/components · error · Error
setContenteditableValue can only be called on a `contentedit
Error message
setContenteditableValue can only be called on a `contenteditable` element.
What it means
UnitTestElement.setContenteditableValue in src/cdk/testing/testbed/unit-test-element.ts is the TestBed (unit-test) implementation of setContenteditableValue. It performs the identical guard — the contenteditable attribute must be '', 'true', or 'plaintext-only' — and throws this Error otherwise. After the guard it stabilizes the fixture and sets element.textContent directly.
Source
Thrown at src/cdk/testing/testbed/unit-test-element.ts:204
if (options?.exclude) {
return _getTextWithExcludedElements(this.element, options.exclude);
}
return (this.element.textContent || '').trim();
}
/**
* Sets the value of a `contenteditable` element.
* @param value Value to be set on the element.
*/
async setContenteditableValue(value: string): Promise<void> {
const contenteditableAttr = await this.getAttribute('contenteditable');
if (
contenteditableAttr !== '' &&
contenteditableAttr !== 'true' &&
contenteditableAttr !== 'plaintext-only'
) {
throw new Error('setContenteditableValue can only be called on a `contenteditable` element.');
}
await this._stabilize();
this.element.textContent = value;
}
/** Gets the value for the given attribute from the element. */
async getAttribute(name: string): Promise<string | null> {
await this._stabilize();
return this.element.getAttribute(name);
}
/** Checks whether the element has the given class. */
async hasClass(name: string): Promise<boolean> {
await this._stabilize();
return this.element.classList.contains(name);
}
View on GitHub (pinned to 0411926e7d)
Solutions
- Render the element with contenteditable="true" (or bind [attr.contenteditable] and set isEditing = true in the test) before the call.
- Use sendKeys for inputs and textareas; setContenteditableValue is only for contenteditable nodes.
- Trigger change detection (fixture.detectChanges()) after toggling the attribute so the DOM reflects editability.
- Guard the call with getAttribute('contenteditable') to fail with a clearer test assertion instead of the throw.
Example fix
// before
it('sets text', () => { el.setContenteditableValue('x'); });
// after
it('sets text', () => { fixture.componentInstance.isEditing = true; fixture.detectChanges(); el.setContenteditableValue('x'); }); Defensive patterns
Strategy: validation
Validate before calling
// In the unit test, before calling:
el.removeAttribute('contenteditable') === undefined; // ensure present
fixture.componentInstance.isEditing = true;
fixture.detectChanges();
expect(el.getAttribute('contenteditable')).toBe('true'); Type guard
function canSetContenteditable(el: HTMLElement): boolean {
const v = el.getAttribute('contenteditable');
return v === 'true' || v === 'plaintext-only' || v === '';
} Try / catch
try {
await el.setContenteditableValue('new text');
} catch (e) {
if (String(e.message).includes('setContenteditableValue can only be called')) {
fail('Element under test must render with contenteditable="true"');
} else throw e;
} Prevention
- Toggle [attr.contenteditable] state and run fixture.detectChanges() before editing in tests.
- Use sendKeys for input/textarea tests instead of setContenteditableValue.
- Add a small test helper that asserts contenteditable before setting values.
- Render fixtures in the same mode (editing) the component uses in production.
When it happens
Trigger: In unit tests using TestbedHarnessEnvironment/TestElement, calling setContenteditableValue on an element rendered without a contenteditable attribute (or with contenteditable="false"), such as a plain div, span, or form input.
Common situations: Unit tests for custom rich-text/editor components where the editable attribute is bound conditionally ([attr.contenteditable]="isEditing") and the test forgot to enable editing; tests copied from sendKeys examples and adapted to the wrong API.
Related errors
- setContenteditableValue can only be called on a `contentedit
- setContenteditableValue can only be called on a `contentedit
- Cannot retrieve popup content because the combobox is closed
- Could not find tab matching filters: ${JSON.stringify(filter
- ListKeyManager constructed with a signal must receive an inj
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/9cc634a2957689f2.
Report an issue: GitHub.