11import { ComponentFixture , TestBed } from '@angular/core/testing' ;
22import { provideZonelessChangeDetection } from '@angular/core' ;
3- import { provideMockStore } from '@ngrx/store/testing' ;
4- import { describe , it , expect , beforeEach } from 'vitest' ;
3+ import { provideHttpClient } from '@angular/common/http' ;
4+ import { provideHttpClientTesting } from '@angular/common/http/testing' ;
5+ import { ActivatedRoute } from '@angular/router' ;
6+ import { describe , it , expect , beforeEach , vi } from 'vitest' ;
7+ import { Subject , of } from 'rxjs' ;
8+ import { StoreModule } from '@ngrx/store' ;
9+ import { deployAppReducer } from '../../../../store/reducers/deploy-app.reducer' ;
510
6- import { getGitHubAPIURL , GITHUB_API_URL , GitSCMService } from '@stratosui/git' ;
7- import { STORE_TEST_PROVIDERS } from '@stratosui/store/testing' ;
11+ import { getGitHubAPIURL , GITHUB_API_URL , GitSCMService , GitHubSCM } from '@stratosui/git' ;
12+ import {
13+ EntityCatalogHelper ,
14+ EntityCatalogHelpers ,
15+ EntityCatalogTestModule ,
16+ TEST_CATALOGUE_ENTITIES ,
17+ generateStratosEntities ,
18+ } from '@stratosui/store' ;
19+ import { createBasicStoreModule , STORE_TEST_PROVIDERS } from '@stratosui/store/testing' ;
20+ import { generateCFEntities } from '../../../../cf-entity-generator' ;
821import { ApplicationDeploySourceTypes } from '../deploy-application-steps.types' ;
922import { DeployApplicationStep2Component } from './deploy-application-step2.component' ;
1023
@@ -15,22 +28,148 @@ describe('DeployApplicationStep2Component', () => {
1528 beforeEach ( ( ) => {
1629 TestBed . configureTestingModule ( {
1730 imports : [
18- DeployApplicationStep2Component
31+ DeployApplicationStep2Component ,
32+ createBasicStoreModule ( ) ,
33+ StoreModule . forFeature ( 'deployApplication' , deployAppReducer ) ,
34+ EntityCatalogTestModule ,
1935 ] ,
2036 providers : [
21- provideMockStore ( ) ,
2237 ...STORE_TEST_PROVIDERS ,
38+ {
39+ provide : TEST_CATALOGUE_ENTITIES ,
40+ useValue : [
41+ ...generateStratosEntities ( ) ,
42+ ...generateCFEntities ( ) ,
43+ ] ,
44+ } ,
2345 GitSCMService ,
2446 ApplicationDeploySourceTypes ,
2547 { provide : GITHUB_API_URL , useFactory : getGitHubAPIURL } ,
26- provideZonelessChangeDetection ( )
27- ]
48+ {
49+ provide : ActivatedRoute ,
50+ useValue : { snapshot : { queryParams : { } , data : { } , params : { } } } ,
51+ } ,
52+ provideZonelessChangeDetection ( ) ,
53+ provideHttpClient ( ) ,
54+ provideHttpClientTesting ( ) ,
55+ ] ,
2856 } ) ;
57+
58+ const helper = TestBed . inject ( EntityCatalogHelper ) ;
59+ EntityCatalogHelpers . SetEntityCatalogHelper ( helper ) ;
2960 } ) ;
3061
31- // TODO: Fix EntityCatalogHelper initialization to enable component creation test
32- // The component requires EntityCatalogHelper to be initialized, which needs proper entity catalog setup
3362 it ( 'should be defined' , ( ) => {
3463 expect ( DeployApplicationStep2Component ) . toBeDefined ( ) ;
3564 } ) ;
65+
66+ it ( 'creates the component' , ( ) => {
67+ fixture = TestBed . createComponent ( DeployApplicationStep2Component ) ;
68+ component = fixture . componentInstance ;
69+ expect ( component ) . toBeTruthy ( ) ;
70+ } ) ;
71+
72+ describe ( 'applyGithubEnterpriseAndToken (GHE + PAT port)' , ( ) => {
73+ let scmSpy : {
74+ setPublicApi : ReturnType < typeof vi . fn > ;
75+ setAccessToken : ReturnType < typeof vi . fn > ;
76+ clearAccessToken : ReturnType < typeof vi . fn > ;
77+ getType : ReturnType < typeof vi . fn > ;
78+ } ;
79+
80+ beforeEach ( ( ) => {
81+ fixture = TestBed . createComponent ( DeployApplicationStep2Component ) ;
82+ component = fixture . componentInstance ;
83+
84+ scmSpy = {
85+ setPublicApi : vi . fn ( ) ,
86+ setAccessToken : vi . fn ( ) ,
87+ clearAccessToken : vi . fn ( ) ,
88+ getType : vi . fn ( ) . mockReturnValue ( 'github' ) ,
89+ } ;
90+ // Inject a mock SCM so applyGithubEnterpriseAndToken's side effects are observable
91+ // without driving the full deploy-step form.
92+ component . scm = scmSpy as unknown as GitHubSCM ;
93+ } ) ;
94+
95+ const invoke = ( url : string | undefined , token : string | undefined ) =>
96+ ( component as unknown as {
97+ applyGithubEnterpriseAndToken ( u ?: string , t ?: string ) : void ;
98+ } ) . applyGithubEnterpriseAndToken ( url , token ) ;
99+
100+ it ( 'flags an invalid enterprise URL and does not set the public API' , ( ) => {
101+ invoke ( 'not-a-url' , undefined ) ;
102+
103+ expect ( component . isInvalidGithubEnterpriseUrl ) . toBe ( true ) ;
104+ expect ( scmSpy . setPublicApi ) . not . toHaveBeenCalled ( ) ;
105+ } ) ;
106+
107+ it ( 'sets the public API endpoint for a valid enterprise URL' , ( ) => {
108+ invoke ( 'https://github.example.com/api/v3' , undefined ) ;
109+
110+ expect ( component . isInvalidGithubEnterpriseUrl ) . toBe ( false ) ;
111+ expect ( scmSpy . setPublicApi ) . toHaveBeenCalledWith ( 'https://github.example.com/api/v3' ) ;
112+ } ) ;
113+
114+ it ( 'sets the access token when one is provided' , ( ) => {
115+ invoke ( 'https://github.example.com/api/v3' , 'pat-abc123' ) ;
116+
117+ expect ( scmSpy . setAccessToken ) . toHaveBeenCalledWith ( 'pat-abc123' ) ;
118+ expect ( scmSpy . clearAccessToken ) . not . toHaveBeenCalled ( ) ;
119+ } ) ;
120+
121+ it ( 'clears the access token when none is provided' , ( ) => {
122+ invoke ( 'https://github.example.com/api/v3' , '' ) ;
123+
124+ expect ( scmSpy . clearAccessToken ) . toHaveBeenCalled ( ) ;
125+ expect ( scmSpy . setAccessToken ) . not . toHaveBeenCalled ( ) ;
126+ } ) ;
127+
128+ it ( 'ignores an empty enterprise URL and leaves the public API untouched' , ( ) => {
129+ invoke ( '' , 'pat-abc123' ) ;
130+
131+ expect ( component . isInvalidGithubEnterpriseUrl ) . toBe ( false ) ;
132+ expect ( scmSpy . setPublicApi ) . not . toHaveBeenCalled ( ) ;
133+ } ) ;
134+
135+ it ( 'skips token handling when the active SCM is not GitHub (e.g. GitLab)' , ( ) => {
136+ scmSpy . getType . mockReturnValue ( 'gitlab' ) ;
137+ invoke ( 'https://gitlab.example.com/api/v4' , 'pat-should-be-ignored' ) ;
138+
139+ expect ( scmSpy . setAccessToken ) . not . toHaveBeenCalled ( ) ;
140+ expect ( scmSpy . clearAccessToken ) . not . toHaveBeenCalled ( ) ;
141+ } ) ;
142+
143+ it ( 'wires applyGithubEnterpriseAndToken to the sourceSelectionForm valueChanges stream' , ( ) => {
144+ // Mock the NgForm ViewChild with a Subject so we can emit form values
145+ // without standing up the full template-driven reactive form. Also mock
146+ // setupForGit's other upstream collaborators just enough to reach the
147+ // suggestedRepos$ assignment where the tap callback is registered.
148+ const valueChanges = new Subject < Record < string , string | undefined > > ( ) ;
149+ ( component as unknown as { sourceSelectionForm : { valueChanges : Subject < unknown > } } ) . sourceSelectionForm = {
150+ valueChanges,
151+ } ;
152+ // updateSuggestedRepositories is called inside switchMap; stub it to
153+ // return an empty result so the pipe completes without network work.
154+ vi . spyOn (
155+ component as unknown as { updateSuggestedRepositories : ( n : string ) => unknown } ,
156+ 'updateSuggestedRepositories' ,
157+ ) . mockReturnValue ( of ( [ ] ) ) ;
158+
159+ // Invoke setupForGit so the valueChanges subscription gets wired up.
160+ ( component as unknown as { setupForGit ( ) : void } ) . setupForGit ( ) ;
161+ // Subscribe to keep the pipe hot; the tap callback only fires on
162+ // subscribed observables.
163+ component . suggestedRepos$ . subscribe ( ) ;
164+
165+ valueChanges . next ( {
166+ githubEnterpriseUrl : 'https://github.example.com/api/v3' ,
167+ githubAccessToken : 'pat-from-form' ,
168+ projectName : 'org/repo' ,
169+ } ) ;
170+
171+ expect ( scmSpy . setPublicApi ) . toHaveBeenCalledWith ( 'https://github.example.com/api/v3' ) ;
172+ expect ( scmSpy . setAccessToken ) . toHaveBeenCalledWith ( 'pat-from-form' ) ;
173+ } ) ;
174+ } ) ;
36175} ) ;
0 commit comments