@@ -22,6 +22,11 @@ describe('API Keys Page', () => {
2222 } ) ,
2323 ) ;
2424
25+ cy . interceptOdh ( 'GET /maas/api/v1/user' , {
26+ data : { userId : 'test-user' , clusterAdmin : false } ,
27+ } ) ;
28+ cy . interceptOdh ( 'GET /maas/api/v1/namespaces' , { data : [ ] } ) ;
29+
2530 cy . interceptOdh (
2631 'GET /api/dsc/status' ,
2732 mockDscStatus ( {
@@ -39,143 +44,115 @@ describe('API Keys Page', () => {
3944 has_more : false ,
4045 } ,
4146 } ) ;
47+ apiKeysPage . visit ( ) ;
48+ } ) ;
4249
43- it ( 'should display the API keys table page' , ( ) => {
44- apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
45- apiKeysPage
46- . findDescription ( )
47- . should (
48- 'contain.text' ,
49- 'Manage personal API keys that can be used to access AI asset endpoints.' ,
50- ) ;
51-
52- apiKeysPage . findTable ( ) . should ( 'exist' ) ;
53- apiKeysPage . findRows ( ) . should ( 'have.length' , 4 ) ;
54-
55- const ciPipelineRow = apiKeysPage . getRow ( 'ci-pipeline' ) ;
56- ciPipelineRow . findName ( ) . should ( 'contain.text' , 'ci-pipeline' ) ;
57- ciPipelineRow
58- . findDescription ( )
59- . should ( 'contain.text' , 'API key for CI/CD pipeline automation' ) ;
60- ciPipelineRow . findStatus ( ) . should ( 'contain.text' , 'Active' ) ;
61- ciPipelineRow . findCreationDate ( ) . should ( 'contain.text' , 'Jan 11, 2026' ) ;
62- ciPipelineRow . findExpirationDate ( ) . should ( 'contain.text' , 'Jan 18, 2026' ) ;
63- apiKeysPage . findTable ( ) . should ( 'exist' ) ;
64- apiKeysPage . findRows ( ) . should ( 'have.length' , 4 ) ;
65- } ) ;
50+ it ( 'should display the API keys table page' , ( ) => {
51+ apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
52+ apiKeysPage
53+ . findDescription ( )
54+ . should (
55+ 'contain.text' ,
56+ 'Manage personal API keys that can be used to access AI asset endpoints.' ,
57+ ) ;
58+
59+ apiKeysPage . findTable ( ) . should ( 'exist' ) ;
60+ apiKeysPage . findRows ( ) . should ( 'have.length' , 4 ) ;
61+
62+ const ciPipelineRow = apiKeysPage . getRow ( 'ci-pipeline' ) ;
63+ ciPipelineRow . findName ( ) . should ( 'contain.text' , 'ci-pipeline' ) ;
64+ ciPipelineRow . findDescription ( ) . should ( 'contain.text' , 'API key for CI/CD pipeline automation' ) ;
65+ ciPipelineRow . findStatus ( ) . should ( 'contain.text' , 'Active' ) ;
66+ ciPipelineRow . findCreationDate ( ) . should ( 'contain.text' , 'Jan 11, 2026' ) ;
67+ ciPipelineRow . findExpirationDate ( ) . should ( 'contain.text' , 'Jan 18, 2026' ) ;
68+ apiKeysPage . findTable ( ) . should ( 'exist' ) ;
69+ apiKeysPage . findRows ( ) . should ( 'have.length' , 4 ) ;
70+ } ) ;
6671
67- it ( 'should bulk revoke all API keys' , ( ) => {
68- apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
69- apiKeysPage . findActionsToggle ( ) . click ( ) ;
70- apiKeysPage . findRevokeAllAPIKeysAction ( ) . click ( ) ;
72+ it ( 'should bulk revoke all API keys' , ( ) => {
73+ apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
74+ apiKeysPage . findActionsToggle ( ) . click ( ) ;
75+ apiKeysPage . findRevokeAllAPIKeysAction ( ) . click ( ) ;
7176
72- bulkRevokeAPIKeyModal . shouldBeOpen ( ) ;
73- bulkRevokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
74- bulkRevokeAPIKeyModal . findRevokeConfirmationInput ( ) . type ( 'incorrect' ) ;
75- bulkRevokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
76- bulkRevokeAPIKeyModal . findRevokeConfirmationInput ( ) . clear ( ) . type ( 'revoke' ) ;
77- bulkRevokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.enabled' ) ;
77+ bulkRevokeAPIKeyModal . shouldBeOpen ( ) ;
78+ bulkRevokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
79+ bulkRevokeAPIKeyModal . findRevokeConfirmationInput ( ) . type ( 'incorrect' ) ;
80+ bulkRevokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
81+ bulkRevokeAPIKeyModal . findRevokeConfirmationInput ( ) . clear ( ) . type ( 'revoke' ) ;
82+ bulkRevokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.enabled' ) ;
7883
79- cy . interceptOdh ( 'POST /maas/api/v1/api-keys/bulk-revoke' , {
80- data : {
81- revokedCount : 4 ,
82- message : 'All API keys revoked' ,
83- } ,
84- } ) . as ( 'deleteAllApiKeys' ) ;
84+ cy . interceptOdh ( 'POST /maas/api/v1/api-keys/bulk-revoke' , {
85+ data : {
86+ revokedCount : 4 ,
87+ message : 'All API keys revoked' ,
88+ } ,
89+ } ) . as ( 'deleteAllApiKeys' ) ;
8590
86- revokeAPIKeyModal . findRevokeButton ( ) . click ( ) ;
87- apiKeysPage . findEmptyState ( ) . should ( 'exist' ) ;
91+ revokeAPIKeyModal . findRevokeAllButton ( ) . click ( ) ;
8892
89- cy . wait ( '@deleteAllApiKeys' ) . then ( ( interception ) => {
90- expect ( interception . response ?. statusCode ) . to . eq ( 200 ) ;
91- } ) ;
93+ cy . wait ( '@deleteAllApiKeys' ) . then ( ( interception ) => {
94+ expect ( interception . response ?. statusCode ) . to . eq ( 200 ) ;
9295 } ) ;
96+ } ) ;
9397
94- it . skip ( 'should revoke api keys' , ( ) => {
95- apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
96- apiKeysPage . findActionsToggle ( ) . click ( ) ;
97- apiKeysPage . findRevokeAllAPIKeysAction ( ) . click ( ) ;
98-
99- revokeAPIKeyModal . shouldBeOpen ( ) ;
100- revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
101- revokeAPIKeyModal . findRevokeConfirmationInput ( ) . type ( 'incorrect' ) ;
102- revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
103- revokeAPIKeyModal . findRevokeConfirmationInput ( ) . clear ( ) . type ( 'revoke' ) ;
104- revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.enabled' ) ;
105-
106- // TODO: fix api calls after uncommenting
107- // cy.interceptOdh('POST /maas/api/v1/api-keys/bulk-revoke', { data: null }).as('deleteAllApiKeys');
108- // cy.interceptOdh('GET /maas/api/v1/api-keys', {
109- // data: [],
110- // }).as('getApiKeysAfterDelete');
111-
112- revokeAPIKeyModal . findRevokeButton ( ) . click ( ) ;
113- apiKeysPage . findEmptyState ( ) . should ( 'exist' ) ;
114-
115- cy . wait ( '@deleteAllApiKeys' ) . then ( ( interception ) => {
116- expect ( interception . response ?. statusCode ) . to . eq ( 200 ) ;
117- } ) ;
118- } ) ;
98+ it ( 'should revoke a specific API key' , ( ) => {
99+ apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
100+ apiKeysPage . getRow ( 'ci-pipeline' ) . findKebabAction ( 'Revoke API key' ) . click ( ) ;
101+
102+ revokeAPIKeyModal . shouldBeOpen ( ) ;
103+ revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
104+ revokeAPIKeyModal . findRevokeConfirmationInput ( ) . type ( 'incorrect' ) ;
105+ revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
106+ revokeAPIKeyModal . findRevokeConfirmationInput ( ) . clear ( ) . type ( 'ci-pipeline' ) ;
107+ revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.enabled' ) ;
119108
120- it ( 'should revoke a specific API key' , ( ) => {
121- apiKeysPage . findTitle ( ) . should ( 'contain.text' , 'API Keys' ) ;
122- apiKeysPage . getRow ( 'ci-pipeline' ) . findKebabAction ( 'Revoke API key' ) . click ( ) ;
123-
124- revokeAPIKeyModal . shouldBeOpen ( ) ;
125- revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
126- revokeAPIKeyModal . findRevokeConfirmationInput ( ) . type ( 'incorrect' ) ;
127- revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.disabled' ) ;
128- revokeAPIKeyModal . findRevokeConfirmationInput ( ) . clear ( ) . type ( 'ci-pipeline' ) ;
129- revokeAPIKeyModal . findRevokeButton ( ) . should ( 'be.enabled' ) ;
130-
131- cy . interceptOdh (
132- 'DELETE /maas/api/v1/api-keys/:id' ,
133- { path : { id : 'key-ci-pipeline-003' } } ,
134- {
135- data : {
136- id : 'key-ci-pipeline-003' ,
137- name : 'ci-pipeline' ,
138- description : 'API key for CI/CD pipeline automation' ,
139- status : 'revoked' ,
140- creationDate : '2026-01-11T11:54:34.521671447-05:00' ,
141- } ,
109+ cy . interceptOdh (
110+ 'DELETE /maas/api/v1/api-keys/:id' ,
111+ { path : { id : 'key-ci-pipeline-003' } } ,
112+ {
113+ data : {
114+ id : 'key-ci-pipeline-003' ,
115+ name : 'ci-pipeline' ,
116+ description : 'API key for CI/CD pipeline automation' ,
117+ status : 'revoked' ,
118+ creationDate : '2026-01-11T11:54:34.521671447-05:00' ,
142119 } ,
143- ) . as ( 'deleteApiKey' ) ;
120+ } ,
121+ ) . as ( 'deleteApiKey' ) ;
144122
145- revokeAPIKeyModal . findRevokeButton ( ) . click ( ) ;
123+ revokeAPIKeyModal . findRevokeButton ( ) . click ( ) ;
146124
147- cy . wait ( '@deleteApiKey' ) . then ( ( interception ) => {
148- expect ( interception . response ?. statusCode ) . to . eq ( 200 ) ;
149- } ) ;
125+ cy . wait ( '@deleteApiKey' ) . then ( ( interception ) => {
126+ expect ( interception . response ?. statusCode ) . to . eq ( 200 ) ;
150127 } ) ;
128+ } ) ;
151129
152- it ( 'should create a new API key' , ( ) => {
153- const now = new Date ( 2026 , 0 , 14 ) . getTime ( ) ; // January 14, 2026
154- // Set the clock to the same day every time so the expiration date is always the same
155- cy . clock ( now ) ;
156-
157- cy . interceptOdh ( 'POST /maas/api/v1/api-keys' , {
158- data : mockCreateAPIKeyResponse ( ) ,
159- } ) . as ( 'createApiKey' ) ;
160-
161- apiKeysPage . findCreateApiKeyButton ( ) . click ( ) ;
162- createApiKeyModal . shouldBeOpen ( ) ;
163- createApiKeyModal . findNameInput ( ) . type ( 'production-backend' ) ;
164- createApiKeyModal . findDescriptionInput ( ) . type ( 'Production API key for backend service' ) ;
165- createApiKeyModal . findExpirationDateInput ( ) . type ( '2026-01-20' ) ;
166- createApiKeyModal . findCreateButton ( ) . click ( ) ;
167- cy . wait ( '@createApiKey' ) . then ( ( interception ) => {
168- expect ( interception . response ?. body ?. data ) . to . include ( {
169- name : 'production-backend' ,
170- expiresAt : '2026-01-20T11:54:34.521671447-05:00' ,
171- } ) ;
130+ it ( 'should create a new API key' , ( ) => {
131+ const now = new Date ( 2026 , 0 , 14 ) . getTime ( ) ; // January 14, 2026
132+ // Set the clock to the same day every time so the expiration date is always the same
133+ cy . clock ( now ) ;
134+
135+ cy . interceptOdh ( 'POST /maas/api/v1/api-keys' , {
136+ data : mockCreateAPIKeyResponse ( ) ,
137+ } ) . as ( 'createApiKey' ) ;
138+
139+ apiKeysPage . findCreateApiKeyButton ( ) . click ( ) ;
140+ createApiKeyModal . shouldBeOpen ( ) ;
141+ createApiKeyModal . findNameInput ( ) . type ( 'production-backend' ) ;
142+ createApiKeyModal . findDescriptionInput ( ) . type ( 'Production API key for backend service' ) ;
143+ createApiKeyModal . findExpirationDateInput ( ) . type ( '2026-01-20' ) ;
144+ createApiKeyModal . findCreateButton ( ) . click ( ) ;
145+ cy . wait ( '@createApiKey' ) . then ( ( interception ) => {
146+ expect ( interception . response ?. body ?. data ) . to . include ( {
147+ name : 'production-backend' ,
148+ expiresAt : '2026-01-20T11:54:34.521671447-05:00' ,
172149 } ) ;
173-
174- copyApiKeyModal . shouldBeOpen ( ) ;
175- // Verify the token is displayed correctly in the ClipboardCopy input
176- copyApiKeyModal . findApiKeyTokenInput ( ) . should ( 'have.value' , mockCreateAPIKeyResponse ( ) . key ) ;
177- copyApiKeyModal . findApiKeyName ( ) . should ( 'contain.text' , 'production-backend' ) ;
178- copyApiKeyModal . findApiKeyExpirationDate ( ) . should ( 'contain.text' , '2026-01-20' ) ;
179150 } ) ;
151+
152+ copyApiKeyModal . shouldBeOpen ( ) ;
153+ // Verify the token is displayed correctly in the ClipboardCopy input
154+ copyApiKeyModal . findApiKeyTokenInput ( ) . should ( 'have.value' , mockCreateAPIKeyResponse ( ) . key ) ;
155+ copyApiKeyModal . findApiKeyName ( ) . should ( 'contain.text' , 'production-backend' ) ;
156+ copyApiKeyModal . findApiKeyExpirationDate ( ) . should ( 'contain.text' , '2026-01-20' ) ;
180157 } ) ;
181158} ) ;
0 commit comments