Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
2d72545
FORMS-23760 : added merging feature for header cells (#1951)
armaang1729 Aug 20, 2026
258c66c
FORMS-23760 : Made the preview and runtime same for col width propert…
armaang1729 Aug 21, 2026
f32df26
feat(Table) : Table component theme variant support (stripped rows + …
armaang1729 Aug 21, 2026
0cb2220
feat(FormContainer): emit fd:setPropertyBehaviour form property (#1968)
rismehta Aug 26, 2026
c99faaa
FORMS - 23760 : Added Table component Mobile Layout support with sort…
armaang1729 Aug 27, 2026
fea6e47
fix: build jsdocs via frontend-maven-plugin instead of exec-maven-plu…
vladbailescu Sep 9, 2026
33bf503
FORMS-24568 Adding aep submit action for Submission service
shraddhasingh683 Sep 9, 2026
311410e
Merge pull request #1983 from adobe/shr/aepccsupport
shraddhasingh683 Sep 9, 2026
a43db2f
feat(recaptcha): support reCAPTCHA v3 in adaptive forms
Aug 28, 2026
c0ab500
fix(recaptcha): register custom functions into XFA runtime at bundle …
kartikey19427 Sep 2, 2026
f6b9b79
fix(container): guard submit/save handlers against missing state.even…
kartikey19427 Sep 2, 2026
659c333
refactor(recaptcha): drop submitForm override; v3 via captchaDisplayM…
kartikey19427 Sep 11, 2026
611c04f
refactor(recaptcha): delegate v3 token fetch to @aemforms/af-custom-f…
kartikey19427 Sep 11, 2026
2911ca8
test(recaptcha): add v3 submission cypress test and real site key
kartikey19427 Sep 14, 2026
33e0f1f
fix(recaptcha): force-set v3 threshold field hidden under ent-fields …
kartikey19427 Sep 15, 2026
29d9882
fix(recaptcha): target inner input for v3 threshold field in cypress …
kartikey19427 Sep 15, 2026
d80dc19
chore(deps): bump @aemforms/af-custom-functions to 1.0.18 for reCAPTC…
kartikey19427 Sep 15, 2026
61c2ea0
test(recaptcha): use Google's public test key for v3 submission test
kartikey19427 Sep 15, 2026
aed05a9
revert(recaptcha): restore real v3 site/secret key, lower threshold to 0
kartikey19427 Sep 17, 2026
952c52b
test(recaptcha): add v3 submission failure cypress test
kartikey19427 Sep 18, 2026
00fe61b
revert(recaptcha): drop bundle-load custom-function registration
kartikey19427 Sep 18, 2026
2b015f5
test(recaptcha): enable FT_FORMS-27648 for reCAPTCHA v3 IT tests
kartikey19427 Sep 18, 2026
041d84c
test(recaptcha): gate v3 tests on FT_FORMS-27648 + latest addon
kartikey19427 Sep 18, 2026
d1ee2bc
docs(recaptcha): document v3 support in the component README
kartikey19427 Sep 18, 2026
d142415
Merge pull request #1986 from adobe/feature/recaptcha-v3-support-dev
kartikey19427 Sep 18, 2026
cc570a6
Merge remote-tracking branch 'origin/dev' into sync/dev-into-master-2…
kartikey19427 Sep 18, 2026
ad728f6
Merge pull request #1988 from adobe/sync/dev-into-master-20260918
mayankjindalmj Sep 18, 2026
0a60bf2
@releng [maven-scm] :prepare release core-forms-components-reactor-3.…
Sep 18, 2026
87aec02
@releng [maven-scm] :prepare for next development iteration
Sep 18, 2026
039f02e
Add Next Gen Dynamic Media (NGDM) support to the StaticImage componen…
lovelymandal16 Sep 28, 2026
f7ab9d0
Add Next Gen Dynamic Media (NGDM) support to the StaticImage componen…
lovelymandal16 Sep 28, 2026
2c7be46
@releng [maven-scm] :prepare release core-forms-components-reactor-3.…
lovelymandal16 Sep 28, 2026
5b1e050
@releng [maven-scm] :prepare for next development iteration
lovelymandal16 Sep 28, 2026
f5c75dd
FORMS-27923: Fix captcha validation failure for existing forms (#2003)
Ikshu-Jain-1 Sep 30, 2026
c756804
Merge pull request #2004 from adobe/dev
rismehta Oct 1, 2026
b9026ad
@releng [maven-scm] :prepare release core-forms-components-reactor-3.…
Oct 1, 2026
03a737a
@releng [maven-scm] :prepare for next development iteration
Oct 1, 2026
cf5211b
Merge pull request #1985 from adobe/feat/webmcp-runtime
rismehta Oct 2, 2026
fb356ae
Merge adobe/dev into master
rismehta Oct 2, 2026
89f9552
Merge pull request #1985 from adobe/feat/webmcp-runtime (#2005)
rismehta Oct 2, 2026
773f049
@releng [maven-scm] :prepare release core-forms-components-reactor-3.…
rismehta Oct 2, 2026
af4f3ef
@releng [maven-scm] :prepare for next development iteration
rismehta Oct 2, 2026
efdc515
fix(tests): isolate telephone policy authoring from component insertion
rismehta Oct 2, 2026
919bde2
fix(tests): synchronize blank-page telephone policy authoring
rismehta Oct 2, 2026
56097a5
Update README.md
rismehta Oct 2, 2026
fc839c7
Update VERSIONS.md
rismehta Oct 2, 2026
717917d
Fix shared authoring synchronization and flaky Sites replacement tests
rismehta Oct 2, 2026
f52ad85
Reuse shared insertion flow and make authoring selectors backport-fri…
rismehta Oct 2, 2026
8be119c
Merge pull request #2006 from adobe/fix/telephoneinput-policy-test
rismehta Oct 2, 2026
f7de780
fix: consume af-core 1.0.10 and cover legacy custom-function access (…
rismehta Oct 23, 2026
ab23168
Merge dev into master
rismehta Oct 23, 2026
38688fa
Merge dev into master: af-core 1.0.10 and authoring test fixes (#2010)
rismehta Oct 23, 2026
d99db3e
@releng [maven-scm] :prepare release core-forms-components-reactor-3.…
rismehta Oct 23, 2026
799688b
@releng [maven-scm] :prepare for next development iteration
rismehta Oct 23, 2026
6208633
Update VERSIONS.md and README.md to include version 3.0.174
Oct 23, 2026
b643784
Merge adobe/master into release/650
rismehta Oct 26, 2026
9e6517e
Exclude CAPTCHA updates from release/650 merge
rismehta Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ private ReservedProperties() {
public static final String FD_DRAFT_ID = "fd:draftId";
public static final String FD_CHANGE_EVENT_BEHAVIOUR = "fd:changeEventBehaviour";
public static final String FD_SET_PROPERTY_BEHAVIOUR = "fd:setPropertyBehaviour";
public static final String FD_WEB_MCP_ENABLED = "fd:webMcpEnabled";

public static final String PN_CQ_ANNOTATIONS = "cq:annotations";

/**
* Comma-separated proportional column widths JCR property, also passed into fd:dor for DOR table rendering (matches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ public class FormContainerImpl extends AbstractContainerImpl implements FormCont
@Nullable
private String setPropertyBehaviour;

@ValueMapValue(name = ReservedProperties.FD_WEB_MCP_ENABLED, injectionStrategy = InjectionStrategy.OPTIONAL)
@Nullable
private Boolean webMcpEnabled;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_DATA)
@Nullable
private String data;
Expand Down Expand Up @@ -447,6 +451,9 @@ public String getLanguageDirection() {
if (StringUtils.isNotBlank(setPropertyBehaviour)) {
properties.put(ReservedProperties.FD_SET_PROPERTY_BEHAVIOUR, setPropertyBehaviour);
}
if (webMcpEnabled != null) {
properties.put(ReservedProperties.FD_WEB_MCP_ENABLED, webMcpEnabled);
}
properties.put(FD_FORM_DATA_ENABLED, formDataEnabled);
if (this.autoSaveConfig != null && this.autoSaveConfig.isEnableAutoSave()) {
properties.put(ReservedProperties.FD_AUTO_SAVE_PROPERTY_WRAPPER, this.autoSaveConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,46 @@ void testSetPropertyBehaviourAbsentWhenNotAuthored() throws Exception {
assertNull(formContainer.getProperties().get(ReservedProperties.FD_SET_PROPERTY_BEHAVIOUR));
}

@Test
void testWebMcpEnabledAbsentWhenNotAuthored() throws Exception {
FormContainer formContainer = Utils.getComponentUnderTest(PATH_FORM_1, FormContainer.class, context);
assertFalse(formContainer.getProperties().containsKey(ReservedProperties.FD_WEB_MCP_ENABLED));
JsonNode properties = new ObjectMapper().readTree(Utils.getJson(formContainer, Views.Publish.class)).get("properties");
assertFalse(properties.has(ReservedProperties.FD_WEB_MCP_ENABLED));
}

@Test
void testWebMcpEnabledFromNode() throws Exception {
assertWebMcpEnabledExport(true, true);
}

@Test
void testWebMcpDisabledFromNode() throws Exception {
assertWebMcpEnabledExport(false, false);
}

@Test
void testWebMcpEnabledFromStringNode() throws Exception {
assertWebMcpEnabledExport("true", true);
}

@Test
void testWebMcpDisabledFromStringNode() throws Exception {
assertWebMcpEnabledExport("false", false);
}

private void assertWebMcpEnabledExport(Object authoredValue, boolean enabled) throws Exception {
Resource resource = context.resourceResolver().getResource(PATH_FORM_1);
resource.adaptTo(ModifiableValueMap.class).put(ReservedProperties.FD_WEB_MCP_ENABLED, authoredValue);
FormContainer formContainer = Utils.getComponentUnderTest(PATH_FORM_1, FormContainer.class, context);
assertEquals(enabled, formContainer.getProperties().get(ReservedProperties.FD_WEB_MCP_ENABLED));
JsonNode property = new ObjectMapper().readTree(Utils.getJson(formContainer, Views.Publish.class))
.get("properties").get(ReservedProperties.FD_WEB_MCP_ENABLED);
assertNotNull(property);
assertTrue(property.isBoolean());
assertEquals(enabled, property.booleanValue());
}

@Test
void testCustomFunctionUrl() throws Exception {
FormContainer formContainer = Utils.getComponentUnderTest(PATH_FORM_1, FormContainer.class, context);
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ curl -s -u admin:admin \
| `cy.previewForm(pagePath)` | Loads form at `<pagePath>?wcmmode=disabled`, waits for AF runtime init, returns `formContainer` |
| `cy.previewFormWithPanel(pagePath)` | Same as above but waits for panel-specific initialization |
| `cy.fetchFeatureToggles()` | `GET /etc.clientlibs/toggles.json`, returns `{ enabled: ["FT_FORMS-XXXXX", ...] }` |
| `cy.selectLayer(layer)` | Keeps an already-selected layer; otherwise waits for the selected layer and overlay repositioning |
| `cy.insertComponent(dropZone, searchText, resourceType)` | Opens the Insert dialog with bounded retries, searches its visible component region, and waits for insertion and overlay refresh |

Authoring specs should reuse these commands instead of implementing their own layer-switch or insertion sequence. Insert-dialog selectors use `guideSelectors.insertComponentDialog.dialog` (`.InsertComponentDialog-components`) and replacement selectors use the Forms hook `.cmp-replace-dialog-search-components`; visibility is scoped to those content regions without depending on a `coral-dialog` element or an outer dialog wrapper.

### Key `cy.af` helpers (defined in `libs/support/functions.js`)

Expand Down
11 changes: 8 additions & 3 deletions docs/e2e-testing/feature-toggles.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ it/config/src/main/content/jcr_root/apps/system/config/
com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json
```

Add the toggle name to the `enabled` array in that file to turn it on in IT.
Add the toggle name to the `enabledToggles` array in that file to turn it on in IT.

### Layer 2 — JVM System Property (server-side)

Expand Down Expand Up @@ -77,7 +77,7 @@ When adding a new toggle, the only config file that requires an update is:

| File | Change |
|------|--------|
| `com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json` | Add toggle name to `enabled` array |
| `com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json` | Add toggle name to `enabledToggles` array |

---

Expand Down Expand Up @@ -178,6 +178,11 @@ it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples
|------|----------------|
| `ui.tests/test-module/specs/fragment/fragment.featuretoggles.cy.js` | FT_FORMS-24087, FT_FORMS-24343, FT_FORMS-24358 |
| `ui.tests/test-module/specs/panelcontainer/panelcontainer.featuretoggles.cy.js` | FT_FORMS-24358 |
| `ui.tests/test-module/specs/webmcp/webmcp.authoring.cy.js` | FT_FORMS-28233 (latest addon) |

The enabled WebMCP authoring suite runs on the latest addon, uses the existing accessibility sample, enables **Enable AI assistant access (WebMCP)** in the container dialog, and opens the saved form in preview. A preview-scoped response interceptor installs a test-only `document.modelContext` host before runtime scripts load; the real runtime must register `list_forms` plus all 12 form tools and return the accessibility form's field summary. Authenticated browser cleanup restores the original opt-in property and its type, or deletes it when originally absent.

Running the authoring flow requires matching Java models and standard/XFA runtime clientlibs, IT content, the examples page component package, and the IT toggle configuration. The instance must have the development toggle provider installed and expose `FT_FORMS-28233` in `/etc.clientlibs/toggles.json`; configuring `enabledToggles` without an active provider does not enable the flag. When the flag is absent, including in without-FT jobs, the suite is reported as skipped without changing the opt-in property. With the flag enabled, the full authoring and preview assertions run.

---

Expand Down Expand Up @@ -219,7 +224,7 @@ curl -s -u admin:admin "http://localhost:4502/content/forms/af/core-components-i

1. **Define the constant** — add `public static final String FT_FORMS_XXXXX = "FT_FORMS-XXXXX";` to `FeatureToggleConstants.java`.

2. **Enable in Granite config** — add `"FT_FORMS-XXXXX"` to the `enabled` array in `DynamicToggleProviderImpl.cfg.json`.
2. **Enable in Granite config** — add `"FT_FORMS-XXXXX"` to the `enabledToggles` array in `DynamicToggleProviderImpl.cfg.json`.

3. **Provision test content** — decide whether to reuse an existing IT sample page or create a new one under `it/content/.../samples/`. Create new JCR content only when the toggled behavior requires it.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,12 @@ function removePanelInstance(panel,globals)
globals.functions.dispatchEvent(p, 'initialize');
})
}

/**
* Hides a field's parent section using legacy private model access.
* @name hideParentSectionUsingPrivateModel
* @param {object} hideSom field in the section to hide
*/
function hideParentSectionUsingPrivateModel(hideSom) {
hideSom._options.parent._setProperty('visible', false);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"FT_FORMS-17107",
"FT_FORMS-24087",
"FT_FORMS-24343",
"FT_FORMS-25252"
"FT_FORMS-25252",
"FT_FORMS-28233"
],
"disabledToggles": [
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
jcr:language="en"
jcr:primaryType="cq:PageContent"
jcr:title="Adaptive Form V2 (IT)"
sling:resourceType="forms-components-examples/components/page">
sling:resourceType="forms-core-components-it/components/page">
<guideContainer
fd:version="2.1"
jcr:primaryType="nt:unstructured"
Expand Down Expand Up @@ -266,6 +266,30 @@
jcr:primaryType="nt:unstructured"
click="[testRemoveInstance()]"/>
</button_476277589>
<legacyAddressSection
jcr:primaryType="nt:unstructured"
jcr:title="Legacy Address Section"
sling:resourceType="forms-components-examples/components/form/panelcontainer"
fieldType="panel"
name="legacyAddressSection"
visible="{Boolean}true">
<streetNumber
jcr:primaryType="nt:unstructured"
jcr:title="Street Number"
sling:resourceType="forms-components-examples/components/form/textinput"
fieldType="text-input"
name="streetNumber"/>
</legacyAddressSection>
<hideLegacyAddressSection
jcr:primaryType="nt:unstructured"
jcr:title="Hide Legacy Address Section"
sling:resourceType="forms-components-examples/components/form/button"
fieldType="button"
name="hideLegacyAddressSection">
<fd:events
jcr:primaryType="nt:unstructured"
click="[hideParentSectionUsingPrivateModel(legacyAddressSection.streetNumber)]"/>
</hideLegacyAddressSection>
</guideContainer>
</jcr:content>
</jcr:root>
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@
text="Enable the hamburger menu for mobile view"
fieldDescription="Select the option to display the hamburger menu for efficient navigation on mobile devices."
value="{Boolean}true"/>
<webMcpEnabled
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./fd:webMcpEnabled"
text="Enable AI assistant access (WebMCP)"
fieldDescription="Allows supported browser AI assistants to securely understand and help users complete this form through WebMCP tools. When enabled, assistants can read form fields and, with user confirmation, update values or submit the form."
uncheckedValue="{Boolean}false"
value="{Boolean}true">
<granite:rendercondition
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/renderconditions/featuretoggle"
toggleName="FT_FORMS-28233"/>
</webMcpEnabled>
<fieldType
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
this._model.subscribe((action) => {
let state = action.target.getState();
// execute the handler only if there are no rules configured on submitSuccess event.
if (!state.events.submitSuccess || state.events.submitSuccess.length === 0) {
// getState() omits `events` in the XFA runtime, so guard the access (optional
// chaining) to avoid a TypeError; a missing events object means no rule is configured.
if (!state.events?.submitSuccess || state.events.submitSuccess.length === 0) {
const globals = {
form: self.getModel().getRuleNode(),
event: {
Expand All @@ -48,7 +50,7 @@
this._model.subscribe((action) => {
let state = action.target.getState();
// execute the handler only if there are no rules configured on submitError event.
if (!state.events.submitError || state.events.submitError.length === 0) {
if (!state.events?.submitError || state.events.submitError.length === 0) {
let defaultSubmissionError = FormView.LanguageUtils.getTranslatedString(self.getLang(), "InternalFormSubmissionError");
const globals = {
form: self.getModel().getRuleNode(),
Expand All @@ -64,15 +66,15 @@
this._model.subscribe((action) => {
let state = action.target.getState();
// execute the handler only if there are no rules configured on custom:saveSuccess event.
if (!state.events['custom:saveSuccess'] || state.events['custom:saveSuccess'].length === 0) {
if (!state.events?.['custom:saveSuccess'] || state.events['custom:saveSuccess'].length === 0) {
console.log("Draft id = " + action?.payload?.body?.draftId);
window.alert(FormView.LanguageUtils.getTranslatedString(self.getLang(), "saveDraftSuccessMessage"));
}
}, "saveSuccess");
this._model.subscribe((action) => {
let state = action.target.getState();
// execute the handler only if there are no rules configured on custom:saveError event.
if (!state.events['custom:saveError'] || state.events['custom:saveError'].length === 0) {
if (!state.events?.['custom:saveError'] || state.events['custom:saveError'].length === 0) {
window.alert(FormView.LanguageUtils.getTranslatedString(self.getLang(), "saveDraftErrorMessage"));
}
}, "saveError");
Expand Down
6 changes: 6 additions & 0 deletions ui.frontend/__tests__/guidebridge.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
******************************************************************************/

jest.mock('@aemforms/af-webmcp', () => ({registerFormWebMCP: jest.fn()}));

import {Constants} from "../src/index";
import FormContainer from "../src/view/FormContainer";
import formJson from './resources/form.json';
Expand Down Expand Up @@ -53,14 +55,18 @@ test('GuideBridge test', () => {
const resetButton = resultModel.getElement("reset-c7cccb1a5e");
expect(resetButton).not.toBeNull();
expect(resetButton.visible).toBeFalsy();
guideBridge.unloadAdaptiveForm("/a/b/c");
expect(unregisterWebMcp).toHaveBeenCalledTimes(1);
}});

});
const unregisterWebMcp = jest.fn();
var formContainer = new FormContainer({
_formJson: formJson,
_prefillData: {data: {"textinput1662370110841" : "initialData"}},
_path: "/a/b/c"
});
formContainer._setWebMcpUnregister(unregisterWebMcp);
const event = new CustomEvent(Constants.FORM_CONTAINER_INITIALISED, { "detail": formContainer });
document.dispatchEvent(event);
});
91 changes: 91 additions & 0 deletions ui.frontend/__tests__/webmcp.integration.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright 2026 Adobe
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

import {createFormInstance} from '@aemforms/af-core';
import {registerFormWebMCP} from '@aemforms/af-webmcp';
import Utils from '../src/utils';

describe('published WebMCP runtime integration', () => {
let unregister;
let tools;
let handles;
let host;
let rum;

beforeEach(() => {
tools = new Map();
handles = [];
host = {registerTool: jest.fn(tool => {
tools.set(tool.name, tool);
const handle = {unregister: jest.fn(() => tools.delete(tool.name))};
handles.push(handle);
return handle;
})};
document.modelContext = host;
rum = jest.fn();
window.hlx = {sampleRUM: rum};
});

afterEach(() => {
if (unregister) { unregister(); }
unregister = undefined;
delete document.modelContext;
delete window.hlx;
document.body.innerHTML = '';
});

function form(enabled) {
return createFormInstance({
id: 'published-runtime',
properties: {'fd:webMcpEnabled': enabled},
items: [{id: 'name', name: 'name', type: 'string', fieldType: 'text-input', value: 'before'}]
});
}

test('registers actual tools, changes values, bridges DOM focus, and cleans up', async () => {
const model = form(true);
const input = document.createElement('input');
document.body.appendChild(input);
const view = {setFocus: jest.fn(() => input.focus())};
unregister = registerFormWebMCP(model, Utils.getWebMcpOptions(view));

expect(tools.get('validate_form_completeness').annotations)
.toEqual(expect.objectContaining({readOnlyHint: true, untrustedContentHint: true}));
expect(await tools.get('set_field_value').execute({field: 'name', value: 'after'}))
.toEqual(expect.objectContaining({success: true, changed: true}));
expect(model.getElement('name').value).toBe('after');
expect(rum).toHaveBeenCalledWith('fill', {source: 'af-webmcp', target: 'set_field_value'});
rum.mockClear();
await tools.get('set_field_value').execute({field: 'name', value: 'after'});
expect(rum).not.toHaveBeenCalled();

expect((await tools.get('focus_field').execute({field: 'name'})).success).toBe(true);
expect(view.setFocus).toHaveBeenCalledWith('name');
expect(document.activeElement).toBe(input);
expect(rum).toHaveBeenCalledWith('click', {source: 'af-webmcp', target: 'focus_field'});

unregister();
expect(tools.size).toBe(0);
handles.forEach(handle => expect(handle.unregister).toHaveBeenCalledTimes(1));
unregister = undefined;
});

test('does not expose tools for a form that did not opt in', () => {
unregister = registerFormWebMCP(form(false));
expect(host.registerTool).not.toHaveBeenCalled();
expect(tools.size).toBe(0);
});
});
Loading
Loading