From a1bbf172aad8443bfbb1859760d62669d6705189 Mon Sep 17 00:00:00 2001
From: chen21019 <19357113+chen21019@users.noreply.github.com>
Date: Sun, 4 Oct 2026 12:00:23 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=BE=A9=E5=8E=9F=E7=94=9F=E9=81=B8?=
=?UTF-8?q?=E5=96=AE=20mut=20=E7=B6=81=E5=AE=9A=E4=B8=A6=E4=BF=9D=E7=95=99?=
=?UTF-8?q?=E6=88=90=E5=93=A1=E5=84=B2=E5=AD=98=E6=AC=8A=E9=99=90=E9=82=8A?=
=?UTF-8?q?=E7=95=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
COMPATIBILITY.md | 10 +++
README.md | 11 ++++
.../form-balancer-rules/template.hbs | 4 +-
.../machine/driver-amazonec2/template.hbs | 4 +-
.../machine/driver-digitalocean/template.hbs | 6 +-
.../machine/driver-exoscale/template.hbs | 6 +-
.../machine/driver-packet/template.hbs | 6 +-
.../machine/driver-rackspace/template.hbs | 4 +-
.../machine/driver-ubiquity/template.hbs | 4 +-
.../modal-edit-setting/template.hbs | 2 +-
app/components/schema/input-enum/template.hbs | 2 +-
.../schema/input-secret/template.hbs | 2 +-
app/components/view-edit-project/template.hbs | 2 +-
...ass-replacement.node24-ignore-scripts.json | 4 +-
docs/releases/web-console-1.6.176.md | 35 +++++++++++
package-lock.json | 4 +-
package.json | 2 +-
scripts/check-modernization-blockers | 6 +-
scripts/check-ui-console-workspace | 2 +-
scripts/check-ui-critical-high-dependencies | 2 +-
.../view-edit-project-permissions-test.js | 61 ++++++++++++++++++-
.../helpers/legacy-template-action-test.js | 23 ++++++-
22 files changed, 168 insertions(+), 34 deletions(-)
create mode 100644 docs/releases/web-console-1.6.176.md
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 36f0581f25..f9a970d87c 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -4,6 +4,16 @@ Web Console preserves compatible API paths, schema and resource names, action na
Visible branding, product-owned assets, icon identifiers, package metadata, and operator documentation use PastureStack. Historical identifiers remain only where they are server data or protocol contracts and must not be mechanically replaced.
+Candidate `1.6.176` preserves native `mut` setters by composing `action` with
+`fn (mut ...)` in the same 21 existing select bindings. The classic helper's
+event `target.value` conversion is unchanged. Model identities, selected-row
+references, schema choices, member capability checks, metadata/network writes,
+save ownership/finalizers, API payload contracts and authentication are unchanged.
+The shared compatibility helper and dependency graph are unchanged. Exact-source
+CI, publication and packaged UI acceptance are pending; the full matrix remains
+INCOMPLETE and historical HOLDs remain immutable.
+See the [release note](docs/releases/web-console-1.6.176.md).
+
Published `1.6.175` refreshes only the image form's locally owned validation
aggregate after image/locale correction. Other validation errors and backend
save failures are preserved; shared NewOrEdit ownership, hook/finalizer behavior,
diff --git a/README.md b/README.md
index d973bf132a..2d161cde10 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,17 @@ PastureStack is an independent community effort to preserve, audit, and moderniz
## Project status
+Candidate `1.6.176` repairs native select bindings that passed a `mut` reference
+directly to the classic compatibility `action` helper. The helper receives the
+current value instead of a setter; wrap `mut` in native `fn` to retain the setter
+while preserving the existing event value mapping. This covers the same 21
+select bindings in 11 templates, including project roles, schema fields,
+balancer rules, settings and machine-driver fields. Project member capabilities,
+identities, metadata/network write boundaries, save finalizers and authentication
+remain unchanged. Exact-source CI, immutable publication and packaged QA
+acceptance are pending; historical HOLDs are not promoted and the full matrix
+remains INCOMPLETE. See the [release note](docs/releases/web-console-1.6.176.md).
+
Published `1.6.175` keeps the container/VM form's image validation message in
sync when the operator corrects the image or changes the locale. Only the
form's own validation aggregate is refreshed: model/command errors and later
diff --git a/app/components/form-balancer-rules/template.hbs b/app/components/form-balancer-rules/template.hbs
index 58f8913eef..7725576112 100644
--- a/app/components/form-balancer-rules/template.hbs
+++ b/app/components/form-balancer-rules/template.hbs
@@ -61,7 +61,7 @@
-
+
{{t 'formBalancerRules.access.public'}}
{{t 'formBalancerRules.access.internal'}}
@@ -78,7 +78,7 @@
{{/if}}
-
+
{{#each this.protocolChoices as |proto|}}
{{upper-case proto}}
{{/each}}
diff --git a/app/components/machine/driver-amazonec2/template.hbs b/app/components/machine/driver-amazonec2/template.hbs
index d6148be6d3..5d019f03cd 100644
--- a/app/components/machine/driver-amazonec2/template.hbs
+++ b/app/components/machine/driver-amazonec2/template.hbs
@@ -11,7 +11,7 @@
{{#if this.isStep1}}
-
+
{{#each this.regionChoices as |choice|}}
{{choice}}
{{/each}}
@@ -77,7 +77,7 @@
{{#if this.isStep3}}
-
+
{{#each this.zoneChoices as |choice|}}
{{choice}}
{{/each}}
diff --git a/app/components/machine/driver-digitalocean/template.hbs b/app/components/machine/driver-digitalocean/template.hbs
index e4e86cce99..ec792dfa3e 100644
--- a/app/components/machine/driver-digitalocean/template.hbs
+++ b/app/components/machine/driver-digitalocean/template.hbs
@@ -38,7 +38,7 @@
{{t 'machine.driverDigitalocean.region.label'}}
-
+
{{#each this.regionChoices as |choice|}}
{{choice.name}}
{{/each}}
@@ -55,7 +55,7 @@
{{t 'machine.driverDigitalocean.image.label'}}
-
+
{{#each this.imageChoices as |choice|}}
{{choice.distribution}} {{choice.name}}
{{/each}}
@@ -65,7 +65,7 @@
{{t 'machine.driverDigitalocean.size.label'}}
-
+
{{#each this.filteredSizeChoices as |choice|}}
{{t 'machine.driverDigitalocean.sizeLabel' memoryGb=choice.memoryGb highMem=choice.highMem slug=choice.slug disk=choice.disk vcpus=choice.vcpus}}
{{/each}}
diff --git a/app/components/machine/driver-exoscale/template.hbs b/app/components/machine/driver-exoscale/template.hbs
index 0a566c5fd8..2138488bd3 100644
--- a/app/components/machine/driver-exoscale/template.hbs
+++ b/app/components/machine/driver-exoscale/template.hbs
@@ -120,7 +120,7 @@
{{radio-button selection=this.whichSecurityGroup value="custom"}} {{t 'machine.driverExoscale.securityGroup.custom'}}
{{#if this.isCustomSecurityGroup}}
-
+
{{#each this.allSecurityGroups as |choice|}}
{{choice.name}}
{{/each}}
@@ -181,7 +181,7 @@
{{t 'machine.driverExoscale.instanceProfile.label'}}
-
+
{{#each this.allInstanceProfiles as |choice|}}
{{choice.displaytext}}
{{/each}}
@@ -200,7 +200,7 @@
{{t 'machine.driverExoscale.template.label'}}
-
+
{{#each this.allTemplates as |choice|}}
{{choice}}
{{/each}}
diff --git a/app/components/machine/driver-packet/template.hbs b/app/components/machine/driver-packet/template.hbs
index 845df4d1c5..d63393a894 100644
--- a/app/components/machine/driver-packet/template.hbs
+++ b/app/components/machine/driver-packet/template.hbs
@@ -35,7 +35,7 @@
{{t 'machine.driverPacket.image.label'}}
-
+
{{#each this.osChoices as |choice|}}
{{choice.name}}
{{/each}}
@@ -45,7 +45,7 @@
{{t 'machine.driverPacket.size.label'}}
-
+
{{#each this.planChoices as |choice|}}
{{choice.name}}
{{/each}}
@@ -62,7 +62,7 @@
{{t 'machine.driverPacket.region.label'}}
-
+
{{#each this.facilityChoices as |choice|}}
{{choice.name}}
{{/each}}
diff --git a/app/components/machine/driver-rackspace/template.hbs b/app/components/machine/driver-rackspace/template.hbs
index 789d923865..90d2d77bc8 100644
--- a/app/components/machine/driver-rackspace/template.hbs
+++ b/app/components/machine/driver-rackspace/template.hbs
@@ -36,7 +36,7 @@
{{t 'machine.driverRackspace.region.label'}}
-
+
{{#each this.regionChoices as |choice|}}
{{choice.label}}
{{/each}}
@@ -53,7 +53,7 @@
{{t 'machine.driverRackspace.flavor.label'}}
-
+
{{#each this.flavorChoices as |choice|}}
{{choice.label}}
{{/each}}
diff --git a/app/components/machine/driver-ubiquity/template.hbs b/app/components/machine/driver-ubiquity/template.hbs
index 236772ebc9..4a4c795a7a 100644
--- a/app/components/machine/driver-ubiquity/template.hbs
+++ b/app/components/machine/driver-ubiquity/template.hbs
@@ -91,7 +91,7 @@
{{t 'machine.driverUbiquity.image.label'}}
-
+
{{#each this.allImages as |choice|}}
{{choice.name}}
{{/each}}
@@ -101,7 +101,7 @@
{{t 'machine.driverUbiquity.flavor.label'}}
-
+
{{#each this.allFlavors as |choice|}}
{{choice.name}}
{{/each}}
diff --git a/app/components/modal-edit-setting/template.hbs b/app/components/modal-edit-setting/template.hbs
index 829a3f3770..0efd2a11b9 100644
--- a/app/components/modal-edit-setting/template.hbs
+++ b/app/components/modal-edit-setting/template.hbs
@@ -23,7 +23,7 @@
{{radio-button selection=this.value value="false"}} {{t 'schema.inputBoolean.n'}}
{{else if (eq this.model.kind 'enum')}}
-
+
{{#each this.model.options as |choice|}}
{{choice}}
{{/each}}
diff --git a/app/components/schema/input-enum/template.hbs b/app/components/schema/input-enum/template.hbs
index af6e9d9477..1f2e5e8d40 100644
--- a/app/components/schema/input-enum/template.hbs
+++ b/app/components/schema/input-enum/template.hbs
@@ -1,4 +1,4 @@
-
+
{{t 'schema.inputEnum.option'}}
{{#each this.field.options as |choice|}}
{{choice}}
diff --git a/app/components/schema/input-secret/template.hbs b/app/components/schema/input-secret/template.hbs
index 822bcb61f8..174b910e81 100644
--- a/app/components/schema/input-secret/template.hbs
+++ b/app/components/schema/input-secret/template.hbs
@@ -1,4 +1,4 @@
-
+
{{t 'schema.inputSecret.prompt'}}
{{#each this.filtered as |secret|}}
{{secret.name}}
diff --git a/app/components/view-edit-project/template.hbs b/app/components/view-edit-project/template.hbs
index 0bfc96a23f..8a3548107a 100644
--- a/app/components/view-edit-project/template.hbs
+++ b/app/components/view-edit-project/template.hbs
@@ -99,7 +99,7 @@
{{member.displayType}}
{{#if (and this.showEdit this.canEditMembers)}}
-
+
{{#each this.roleOptions as |choice|}}
{{t choice.label}}
{{/each}}
diff --git a/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json b/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json
index e9ea2e5e99..11f7add2ea 100644
--- a/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json
+++ b/docs/baselines/npm-package-lock.sass-replacement.node24-ignore-scripts.json
@@ -1,12 +1,12 @@
{
"name": "@pasturestack/web-console",
- "version": "1.6.175",
+ "version": "1.6.176",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pasturestack/web-console",
- "version": "1.6.175",
+ "version": "1.6.176",
"license": "Apache-2.0",
"dependencies": {
"sass": "1.103.1"
diff --git a/docs/releases/web-console-1.6.176.md b/docs/releases/web-console-1.6.176.md
new file mode 100644
index 0000000000..e9844b59bd
--- /dev/null
+++ b/docs/releases/web-console-1.6.176.md
@@ -0,0 +1,35 @@
+# Web Console 1.6.176
+
+Candidate source only. Exact-source CI, signed immutable publication, Server
+packaging and deployed UI acceptance are pending. Do not promote historical
+Project HOLDs or infer full-matrix acceptance from this correction.
+
+## Root cause and minimal repair
+
+The native Project member-role select used `action (mut member.role)` with
+`value="target.value"`. Ember 7.2 exposes the current value of an invokable `mut`
+reference to a classic helper. The compatibility `action` helper therefore sees
+the current role string, not the setter, and dispatches that name instead of
+updating the member. The DOM selection can change while the model stays unchanged.
+
+Wrap the existing reference in native `fn`: `action (fn (mut member.role))`.
+The helper now receives a callable setter and retains its existing event value
+mapping. Apply exactly the same composition to 21 selects in 11 templates:
+Project roles, schema enum/secret fields, settings, balancer rules and the six
+affected machine drivers. No shared helper rewrite or unrelated event migration
+is introduced. The existing source gate now also detects this unsafe composition
+in `{{action ...}}`, not only parenthesized subexpressions.
+
+Real-component regressions cover plain cloned and EmberObject Project members,
+sorted selected-row identity, input/change propagation, unchanged owner/metadata,
+members-only native save payload/finalizer and no metadata/network writes. A
+rendered native schema enum checks nested model updates without replacing unknown
+metadata or schema choices. Existing capability tests retain the unavailable
+member-role select when no `setmembers` link exists.
+
+Dependencies and their graph are unchanged; only root version metadata and its
+executable/reviewed-baseline pins become 1.6.176. The Web175 image validation fix,
+session generation, save owner, backend schemas, authorization, OIDC, platform
+MFA and compatibility contracts remain intact. The complete matrix remains
+INCOMPLETE. Retain configuration, volumes and rollback artifacts; this source
+change does not authorize company deployment.
diff --git a/package-lock.json b/package-lock.json
index e9ea2e5e99..11f7add2ea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@pasturestack/web-console",
- "version": "1.6.175",
+ "version": "1.6.176",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pasturestack/web-console",
- "version": "1.6.175",
+ "version": "1.6.176",
"license": "Apache-2.0",
"dependencies": {
"sass": "1.103.1"
diff --git a/package.json b/package.json
index 2238e09614..bcb5c9638a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@pasturestack/web-console",
- "version": "1.6.175",
+ "version": "1.6.176",
"private": true,
"description": "PastureStack browser console for the compatible control platform.",
"repository": {
diff --git a/scripts/check-modernization-blockers b/scripts/check-modernization-blockers
index 49935e6677..c143c9680c 100755
--- a/scripts/check-modernization-blockers
+++ b/scripts/check-modernization-blockers
@@ -41,8 +41,8 @@ with open('package.json', encoding='utf-8') as f:
print(json.load(f).get('version', ''))
PY
)
-if [[ "$version" != "1.6.175" ]]; then
- echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.175"
+if [[ "$version" != "1.6.176" ]]; then
+ echo "UNEXPECTED_UI_ARTIFACT_VERSION version=$version expected=1.6.176"
failures=$((failures + 1))
fi
@@ -59,7 +59,7 @@ require_no_pattern 'class="(loader|orbit|loadfield|grass|sun|moon)"' \
'legacy product loading artwork returned to the application transition' \
app/application/template.hbs
-require_no_pattern '\(action[[:space:]]+\(mut' \
+require_no_pattern '(\{\{|\()action[[:space:]]+\(mut' \
'removed action-mut composition returned instead of the supported fn-mut contract' \
app
diff --git a/scripts/check-ui-console-workspace b/scripts/check-ui-console-workspace
index f767b59139..5c9b782f89 100755
--- a/scripts/check-ui-console-workspace
+++ b/scripts/check-ui-console-workspace
@@ -141,4 +141,4 @@ if [[ -n ${PASTURESTACK_PRIVATE_MARKER:-} ]] && grep -RInF -- "$PASTURESTACK_PRI
fi
printf 'UI_CONSOLE_WORKSPACE_OK version=%s persistence=%s cross_tab=%s\n' \
- 1.6.175 browser-session broker-broadcast
+ 1.6.176 browser-session broker-broadcast
diff --git a/scripts/check-ui-critical-high-dependencies b/scripts/check-ui-critical-high-dependencies
index a72e8b835c..6eafe81910 100755
--- a/scripts/check-ui-critical-high-dependencies
+++ b/scripts/check-ui-critical-high-dependencies
@@ -70,7 +70,7 @@ if lock_bytes != baseline_bytes:
lock = json.loads(lock_bytes)
packages = lock.get("packages", {})
root = packages.get("", {})
-if package.get("version") != "1.6.175":
+if package.get("version") != "1.6.176":
fail(f"unexpected Web Console version: {package.get('version')}")
if root.get("version") != package.get("version"):
fail(f"lock root version differs: {root.get('version')}")
diff --git a/tests/integration/components/view-edit-project-permissions-test.js b/tests/integration/components/view-edit-project-permissions-test.js
index bcca300866..e1d4ac0839 100644
--- a/tests/integration/components/view-edit-project-permissions-test.js
+++ b/tests/integration/components/view-edit-project-permissions-test.js
@@ -1,9 +1,9 @@
import { A } from '@ember/array';
import Component from '@ember/component';
-import EmberObject from '@ember/object';
+import EmberObject, { get } from '@ember/object';
import Service from '@ember/service';
import { precompileTemplate } from '@ember/template-compilation';
-import { click, find, findAll, render, settled, setupContext, setupRenderingContext, teardownContext } from '@ember/test-helpers';
+import { click, find, findAll, render, select, settled, setupContext, setupRenderingContext, teardownContext } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { initialize as initializePodLayouts } from 'ui/initializers/pod-component-layouts';
@@ -67,6 +67,63 @@ module('Integration | Component | view edit project permissions', function(hooks
this.testRoot.remove();
});
+ for (let memberType of ['plain object', 'EmberObject']) {
+ test(`native role select updates the selected ${memberType} without metadata or network writes`, async function(assert) {
+ let attributes = {
+ displayType: 'User', externalId: 'member-2', externalIdType: 'oidc_user',
+ name: 'Alpha Member', role: 'member',
+ };
+ let member = memberType === 'plain object' ? {...attributes} : EmberObject.create(attributes);
+ let owner = {displayType: 'User', externalId: 'owner-1', externalIdType: 'oidc_user', name: 'Zulu Owner', role: 'owner'};
+ let writes = {project: 0, members: [], network: 0, refresh: 0, done: 0};
+ this.project.setProperties({
+ actionLinks: {setmembers: '/projects/1a21?action=setmembers'},
+ projectMembers: A([owner, member]),
+ validationErrors() { return A([]); },
+ save() { writes.project++; return Promise.resolve(this); },
+ doAction(action, payload) {
+ assert.strictEqual(action, 'setmembers', 'the native save submits only memberships');
+ writes.members.push(payload);
+ return Promise.resolve();
+ },
+ });
+ this.network.set('save', () => { writes.network++; return Promise.resolve(); });
+ this.owner.lookup('service:projects').set('refreshAll', () => { writes.refresh++; });
+ this.done = () => { writes.done++; };
+ this.userStore = {getById() { return EmberObject.create({resourceFields: {role: {options: ['owner', 'member', 'readonly']}}}); }};
+
+ await render(precompileTemplate(`{{view-edit-project
+ project=this.project originalProject=this.originalProject network=this.network
+ policyManager=this.policyManager userStore=this.userStore showEdit=true editing=true done=this.done
+ }}`));
+
+ let rows = findAll('table.grid tbody tr');
+ let selectedRow = rows.find((row) => row.querySelector('[data-test-member-name]').textContent.trim() === attributes.name);
+ let roleSelect = selectedRow.querySelector('select');
+ assert.strictEqual(rows[0], selectedRow, 'sorting selects the member rather than its original array index');
+ assert.strictEqual(roleSelect.value, 'member');
+ await select(roleSelect, 'readonly');
+ assert.strictEqual(get(member, 'role'), 'readonly', 'input/change writes the real selected model');
+ assert.strictEqual(this.project.get('projectMembers')[1], member, 'the arranged row retains its original model reference');
+ assert.deepEqual({externalId: get(member, 'externalId'), externalIdType: get(member, 'externalIdType'), name: get(member, 'name')},
+ {externalId: attributes.externalId, externalIdType: attributes.externalIdType, name: attributes.name}, 'identity and label are unchanged');
+ assert.strictEqual(owner.role, 'owner', 'the owner row is not changed');
+ assert.strictEqual(this.project.get('description'), 'Description', 'role selection does not change metadata');
+ assert.true(find('input[type="text"]').disabled, 'members-only capability does not unlock metadata');
+ assert.strictEqual(findAll('.radio input').length, 0, 'members-only capability does not unlock network policy');
+ assert.deepEqual(writes.members, [], 'selecting alone sends no save');
+
+ await click('.footer-actions .btn-primary');
+ assert.deepEqual(writes.members, [{members: [
+ {type: 'projectMember', externalId: owner.externalId, externalIdType: owner.externalIdType, role: 'owner'},
+ {type: 'projectMember', externalId: attributes.externalId, externalIdType: attributes.externalIdType, role: 'readonly'},
+ ]}], 'native save sends the selected role with exact original identities');
+ assert.deepEqual({project: writes.project, network: writes.network, refresh: writes.refresh, done: writes.done},
+ {project: 0, network: 0, refresh: 1, done: 1}, 'save capability and finalizer remain scoped to memberships');
+ assert.strictEqual(owner.role, 'owner');
+ });
+ }
+
test('new environment cards render real native names and select the same ID after a rename', async function(assert) {
this.app = EmberObject.create({baseAssets: '/'});
let zulu = ProjectTemplate.create({id: '1pt-zulu', name: 'Zulu environment', stacks: A([]), app: this.app});
diff --git a/tests/integration/helpers/legacy-template-action-test.js b/tests/integration/helpers/legacy-template-action-test.js
index 86d30b64ad..b30c8079c9 100644
--- a/tests/integration/helpers/legacy-template-action-test.js
+++ b/tests/integration/helpers/legacy-template-action-test.js
@@ -1,6 +1,9 @@
import { module, test } from 'qunit';
-import { fillIn, render, setupContext, setupRenderingContext, teardownContext } from '@ember/test-helpers';
+import { fillIn, find, render, select, setupContext, setupRenderingContext, teardownContext } from '@ember/test-helpers';
import { precompileTemplate } from '@ember/template-compilation';
+import Service from '@ember/service';
+
+import { initialize as initializePodLayouts } from 'ui/initializers/pod-component-layouts';
import resolver from '../../helpers/resolver';
@@ -10,6 +13,8 @@ module('Integration | Helper | legacy template action', function(hooks) {
this.testRoot.id = 'ember-testing';
document.body.appendChild(this.testRoot);
await setupContext(this, { resolver });
+ initializePodLayouts();
+ this.owner.register('service:intl', Service.extend({t(key) { return key; }}));
await setupRenderingContext(this);
});
@@ -27,4 +32,20 @@ module('Integration | Helper | legacy template action', function(hooks) {
await fillIn('input', 'sleep 3600');
assert.deepEqual(this.command, ['sleep', '3600'], 'the rendered command field writes the bound array');
});
+
+ test('native enum select preserves the mut setter and changes only its bound model field', async function(assert) {
+ this.enumModel = {id: 'unchanged-model', value: 'first', metadata: {stable: true}};
+ this.enumField = {options: ['first', 'second']};
+ let metadata = this.enumModel.metadata;
+ await render(precompileTemplate('{{schema/input-enum field=this.enumField value=this.enumModel.value}}'));
+
+ assert.strictEqual(find('select').value, 'first');
+ await select('select', 'second');
+ assert.strictEqual(this.enumModel.value, 'second', 'real input/change invokes the native fn-mut setter');
+ assert.strictEqual(find('select').value, 'second', 'the selected option agrees with the model');
+ assert.strictEqual(this.enumModel.id, 'unchanged-model');
+ assert.strictEqual(this.enumModel.metadata, metadata, 'unknown metadata is not replaced');
+ assert.deepEqual(metadata, {stable: true});
+ assert.deepEqual(this.enumField.options, ['first', 'second'], 'schema choices remain unchanged');
+ });
});