@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
22import { provideZonelessChangeDetection } from '@angular/core' ;
33import { provideNoopAnimations } from '@angular/platform-browser/animations' ;
44import { FormsModule } from '@angular/forms' ;
5- import { BrowserModule , By } from '@angular/platform-browser' ;
5+ import { BrowserModule } from '@angular/platform-browser' ;
66import { of , throwError } from 'rxjs' ;
77import { TranslateModule } from '@ngx-translate/core' ;
88import { MockHandlerService } from '@keira/shared/base-abstract-classes' ;
@@ -13,9 +13,35 @@ import { anything, instance, mock, reset, when } from 'ts-mockito';
1313
1414import { CreateComponent } from './create.component' ;
1515
16+ class CreateComponentPage extends PageObject < CreateComponent < TableRow > > {
17+ constructor ( fixture : ComponentFixture < CreateComponent < TableRow > > ) {
18+ super ( fixture ) ;
19+ }
20+
21+ get idInput ( ) : HTMLInputElement {
22+ return this . query < HTMLInputElement > ( '#id' ) ;
23+ }
24+ get selectBtn ( ) : HTMLInputElement {
25+ return this . query < HTMLInputElement > ( '#select-button' ) ;
26+ }
27+ get idFreeStatusBox ( ) : HTMLDivElement {
28+ return this . query < HTMLDivElement > ( '#id-free-status' ) ;
29+ }
30+ get sourceInput ( ) : HTMLInputElement {
31+ return this . getInputById ( 'source-id' ) ;
32+ }
33+ get copyInput ( ) : HTMLInputElement {
34+ return this . getInputById ( 'method-copy' ) ;
35+ }
36+ get copyRadio ( ) : HTMLInputElement | null {
37+ return this . query < HTMLInputElement > ( '#method-copy' , false ) ;
38+ }
39+ }
40+
1641describe ( 'CreateComponent' , ( ) => {
1742 let component : CreateComponent < any > ;
1843 let fixture : ComponentFixture < CreateComponent < any > > ;
44+ let page : CreateComponentPage ;
1945
2046 const fakeQueryService : any = {
2147 getMaxId : ( ) => of ( [ { max : 5 } ] ) ,
@@ -29,6 +55,7 @@ describe('CreateComponent', () => {
2955 beforeEach ( async ( ) => {
3056 await TestBed . configureTestingModule ( {
3157 imports : [ FormsModule , TranslateModule . forRoot ( ) , CreateComponent ] ,
58+ providers : [ provideZonelessChangeDetection ( ) , provideNoopAnimations ( ) ] ,
3259 } ) . compileComponents ( ) ;
3360
3461 fixture = TestBed . createComponent ( CreateComponent ) ;
@@ -40,23 +67,23 @@ describe('CreateComponent', () => {
4067 component . queryService = fakeQueryService ;
4168 component . handlerService = fakeHandlerService ;
4269 fixture . detectChanges ( ) ;
70+ page = new CreateComponentPage ( fixture ) ;
4371 } ) ;
4472
4573 it ( 'does not render the copy option when allowCopy is false' , ( ) => {
4674 // Default allowCopy is false
4775 fixture . detectChanges ( ) ;
48- const copyRadio = fixture . debugElement . query ( By . css ( '#method-copy' ) ) ;
76+ const copyRadio = page . copyRadio ;
4977 expect ( copyRadio ) . toBeNull ( ) ;
5078 expect ( component . creationMethod ) . toBe ( 'blank' ) ;
5179 } ) ;
5280
5381 it ( 'renders the copy option when allowCopy is true' , ( ) => {
5482 component . allowCopy = true ;
5583 fixture . detectChanges ( ) ;
56- const copyRadio = fixture . debugElement . query ( By . css ( '#method-copy' ) ) ;
57- expect ( copyRadio ) . not . toBeNull ( ) ;
84+ const copyInput = page . copyInput ;
85+ expect ( copyInput ) . toBeDefined ( ) ;
5886 // when allowed the copy radio should not be disabled
59- const copyInput = copyRadio . nativeElement as HTMLInputElement ;
6087 expect ( copyInput . disabled ) . toBeFalse ( ) ;
6188 } ) ;
6289
@@ -76,18 +103,6 @@ describe('CreateComponent', () => {
76103} ) ;
77104// (Additional tests below use the imports declared above)
78105
79- class CreateComponentPage extends PageObject < CreateComponent < TableRow > > {
80- get idInput ( ) : HTMLInputElement {
81- return this . query < HTMLInputElement > ( '#id' ) ;
82- }
83- get selectBtn ( ) : HTMLInputElement {
84- return this . query < HTMLInputElement > ( '#select-button' ) ;
85- }
86- get idFreeStatusBox ( ) : HTMLDivElement {
87- return this . query < HTMLDivElement > ( '#id-free-status' ) ;
88- }
89- }
90-
91106describe ( 'CreateComponent' , ( ) => {
92107 const mockTable = 'mock_table' ;
93108 const mockId = 'mockId' ;
@@ -192,4 +207,106 @@ describe('CreateComponent', () => {
192207 component . customStartingId = 10 ;
193208 expect ( component [ 'calculateNextId' ] ( 5 ) ) . toEqual ( 10 ) ;
194209 } ) ;
210+
211+ it ( 'validates source id and calls select with copy params on create' , async ( ) => {
212+ const { fixture, page, component } = setup ( ) ;
213+
214+ // Enable copying and switch to copy method directly (radio may not always be present in this env)
215+ component . allowCopy = true ;
216+ component . creationMethod = 'copy' ;
217+ fixture . detectChanges ( ) ;
218+ await fixture . whenStable ( ) ;
219+
220+ const sourceRadio = page . copyRadio ;
221+ // Guard: the radio is optional in this test runner, but the source input must be present for copy flow
222+ if ( sourceRadio ) {
223+ ( sourceRadio as HTMLInputElement ) . checked = true ;
224+ sourceRadio . dispatchEvent ( new Event ( 'change' ) ) ;
225+ fixture . detectChanges ( ) ;
226+ }
227+
228+ // Provide an existing source id and trigger input (setup returns an existing item for `takenId`)
229+ const sourceInput = page . sourceInput ;
230+ page . setInputValue ( sourceInput , takenId ) ;
231+ fixture . detectChanges ( ) ;
232+ await fixture . whenStable ( ) ;
233+
234+ expect ( component . isSourceIdValid ) . toBeTrue ( ) ;
235+
236+ // Prepare a new id and create
237+ component . idModel = 2000 ;
238+ component . isIdFree = true ;
239+ fixture . detectChanges ( ) ;
240+
241+ const selectSpy = spyOn ( component . handlerService , 'select' ) ;
242+
243+ page . clickElement ( page . selectBtn ) ;
244+
245+ expect ( selectSpy ) . toHaveBeenCalledTimes ( 1 ) ;
246+ expect ( selectSpy ) . toHaveBeenCalledWith ( true , 2000 , undefined , true , `${ takenId } ` ) ;
247+ } ) ;
248+
249+ it ( 'does not allow a higher source value than max value' , ( ) => {
250+ const { component } = setup ( ) ;
251+ component . sourceIdModel = MAX_INT_UNSIGNED_VALUE + 1 ;
252+
253+ ( component as any ) . checkMaxValue ( ) ;
254+
255+ expect ( component . sourceIdModel ) . toEqual ( MAX_INT_UNSIGNED_VALUE ) ;
256+ } ) ;
257+
258+ it ( 'onCreationMethodChange clears source state when switched to blank' , ( ) => {
259+ const { component } = setup ( ) ;
260+ component . creationMethod = 'copy' ;
261+ component . sourceIdModel = 123 ;
262+ component . isSourceIdValid = true ;
263+
264+ component . creationMethod = 'blank' ;
265+ ( component as any ) . onCreationMethodChange ( ) ;
266+
267+ expect ( component . sourceIdModel ) . toBeUndefined ( ) ;
268+ expect ( component . isSourceIdValid ) . toBeFalse ( ) ;
269+ } ) ;
270+
271+ it ( 'checkSourceId handles errors and marks source invalid' , ( ) => {
272+ const { component, MockedMysqlQueryService } = setup ( ) ;
273+ reset ( MockedMysqlQueryService ) ;
274+ when ( MockedMysqlQueryService . selectAll ( mockTable , mockId , anything ( ) ) ) . thenReturn ( throwError ( 'error' ) ) ;
275+
276+ component . sourceIdModel = 999 ;
277+ ( component as any ) . checkSourceId ( ) ;
278+
279+ expect ( component . isSourceIdValid ) . toBeFalse ( ) ;
280+ expect ( component . loading ) . toBe ( false ) ;
281+ } ) ;
282+
283+ it ( 'checkSourceId early-return when no sourceIdModel' , ( ) => {
284+ const { component } = setup ( ) ;
285+ component . sourceIdModel = undefined ;
286+
287+ ( component as any ) . checkSourceId ( ) ;
288+
289+ expect ( component . isSourceIdValid ) . toBeFalse ( ) ;
290+ } ) ;
291+
292+ it ( 'isFormValid correctly evaluates copy vs blank methods' , ( ) => {
293+ const { component } = setup ( ) ;
294+
295+ // blank creation method
296+ component . creationMethod = 'blank' ;
297+ component . idModel = 1 ;
298+ component . isIdFree = true ;
299+ expect ( ( component as any ) . isFormValid ( ) ) . toBeTrue ( ) ;
300+
301+ // copy method without source data should be invalid
302+ component . creationMethod = 'copy' ;
303+ component . sourceIdModel = undefined ;
304+ component . isSourceIdValid = false ;
305+ expect ( ( component as any ) . isFormValid ( ) ) . toBeFalse ( ) ;
306+
307+ // copy method with valid source should be valid
308+ component . sourceIdModel = 123 ;
309+ component . isSourceIdValid = true ;
310+ expect ( ( component as any ) . isFormValid ( ) ) . toBeTrue ( ) ;
311+ } ) ;
195312} ) ;
0 commit comments