@@ -11,7 +11,7 @@ import Neo from '../../../../src/Neo.mjs';
1111import * as core from '../../../../src/core/_export.mjs' ;
1212import Container from '../../../../src/container/Base.mjs' ;
1313import Component from '../../../../src/component/Base.mjs' ;
14- import Button from '../../../../src/button/Base.mjs' ; // Required for ntype: button
14+ import Button from '../../../../src/button/Base.mjs' ;
1515import { isDescriptor , mergeFrom } from '../../../../src/core/ConfigSymbols.mjs' ;
1616
1717test . describe ( 'Declarative Config Merging' , ( ) => {
@@ -85,4 +85,129 @@ test.describe('Declarative Config Merging', () => {
8585 expect ( btn . text ) . toBe ( 'Overridden Text' ) ;
8686 expect ( btn . iconCls ) . toBe ( 'home' ) ;
8787 } ) ;
88- } ) ;
88+
89+ test ( 'Prototype Pollution: Subclasses should not share state when clone: deep is used' , ( ) => {
90+ // 1. Define Shared Base
91+ class SharedBase extends Container {
92+ static config = {
93+ className : 'Neo.test.Pollution.Shared' ,
94+
95+ commonConfig_ : {
96+ [ isDescriptor ] : true ,
97+ merge : 'deep' ,
98+ value : null // To be overridden
99+ } ,
100+
101+ items : {
102+ [ isDescriptor ] : true ,
103+ merge : 'deep' ,
104+ clone : 'deep' , // CRITICAL for isolation
105+ value : {
106+ child : {
107+ ntype : 'component' ,
108+ [ mergeFrom ] : 'commonConfig'
109+ }
110+ }
111+ }
112+ }
113+ }
114+ SharedBase = Neo . setupClass ( SharedBase ) ;
115+
116+ // 2. Define Subclass A
117+ class ClassA extends SharedBase {
118+ static config = {
119+ className : 'Neo.test.Pollution.ClassA' ,
120+ commonConfig : {
121+ text : 'Text A'
122+ }
123+ }
124+ }
125+ ClassA = Neo . setupClass ( ClassA ) ;
126+
127+ // 3. Define Subclass B
128+ class ClassB extends SharedBase {
129+ static config = {
130+ className : 'Neo.test.Pollution.ClassB' ,
131+ commonConfig : {
132+ text : 'Text B'
133+ }
134+ }
135+ }
136+ ClassB = Neo . setupClass ( ClassB ) ;
137+
138+ // 4. Instantiate A
139+ const instanceA = Neo . create ( ClassA ) ;
140+ const childA = instanceA . items [ 0 ] ;
141+ expect ( childA . text ) . toBe ( 'Text A' ) ;
142+
143+ // 5. Instantiate B
144+ const instanceB = Neo . create ( ClassB ) ;
145+ const childB = instanceB . items [ 0 ] ;
146+
147+ // If pollution occurred, B might see 'Text A' or the mergeFrom symbol might be gone
148+ expect ( childB . text ) . toBe ( 'Text B' ) ;
149+ } ) ;
150+
151+ test ( 'Nested Object Items Pollution Check' , ( ) => {
152+ // Verify recursion + isolation
153+ class NestedBase extends Container {
154+ static config = {
155+ className : 'Neo.test.Pollution.NestedBase' ,
156+
157+ targetConfig_ : {
158+ [ isDescriptor ] : true ,
159+ merge : 'deep' ,
160+ value : null
161+ } ,
162+
163+ items : {
164+ [ isDescriptor ] : true ,
165+ merge : 'deep' ,
166+ clone : 'deep' ,
167+ value : {
168+ wrapper : {
169+ ntype : 'container' ,
170+ items : {
171+ target : {
172+ ntype : 'component' ,
173+ [ mergeFrom ] : 'targetConfig'
174+ }
175+ }
176+ }
177+ }
178+ }
179+ }
180+ }
181+ NestedBase = Neo . setupClass ( NestedBase ) ;
182+
183+ class NestedA extends NestedBase {
184+ static config = {
185+ className : 'Neo.test.Pollution.NestedA' ,
186+ targetConfig : { cls : [ 'class-a' ] }
187+ }
188+ }
189+ NestedA = Neo . setupClass ( NestedA ) ;
190+
191+ class NestedB extends NestedBase {
192+ static config = {
193+ className : 'Neo.test.Pollution.NestedB' ,
194+ targetConfig : { cls : [ 'class-b' ] }
195+ }
196+ }
197+ NestedB = Neo . setupClass ( NestedB ) ;
198+
199+ const a = Neo . create ( NestedA ) ;
200+ // Note: wrapper is a Container. We need to check ITS items.
201+ // But Container items are lazily created or array-ified.
202+ // Wrapper item in 'a' is an instance? No, 'wrapper' in 'a.items' is an instance.
203+ // 'wrapper' instance has 'items'.
204+ const wrapperA = a . items [ 0 ] ;
205+ const targetA = wrapperA . items [ 0 ] ;
206+ expect ( targetA . cls ) . toEqual ( [ 'class-a' ] ) ;
207+
208+ const b = Neo . create ( NestedB ) ;
209+ const wrapperB = b . items [ 0 ] ;
210+ const targetB = wrapperB . items [ 0 ] ;
211+ expect ( targetB . cls ) . toEqual ( [ 'class-b' ] ) ;
212+ } ) ;
213+ } ) ;
0 commit comments