Skip to content

Commit 6cd67dc

Browse files
committed
console: Create e2e tests for api key create modal
1 parent 7288024 commit 6cd67dc

File tree

3 files changed

+197
-19
lines changed

3 files changed

+197
-19
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright © 2025 The Things Network Foundation, The Things Industries B.V.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
describe('Gateway API key modal in overview', () => {
16+
const userId = 'managed-gateway-test-user'
17+
const user = {
18+
ids: { user_id: userId },
19+
primary_email_address: '[email protected]',
20+
password: 'ABCDefg123!',
21+
password_confirm: 'ABCDefg123!',
22+
}
23+
24+
const gatewayId = 'test-gateway'
25+
const gateway = { ids: { gateway_id: gatewayId } }
26+
27+
beforeEach(() => {
28+
cy.dropAndSeedDatabase()
29+
cy.createUser(user)
30+
cy.createGateway(gateway, userId)
31+
32+
cy.intercept('POST', `/api/v3/gateways/test-gateway/api-keys`, {
33+
statusCode: 200,
34+
body: {
35+
id: 'api-key-id',
36+
key: 'GTWAPIKEY',
37+
},
38+
}).as('create-api-key')
39+
40+
cy.loginConsole({ user_id: user.ids.user_id, password: user.password })
41+
cy.visit(`${Cypress.config('consoleRootPath')}/gateways/${gatewayId}`)
42+
cy.findByRole('heading', { name: gatewayId })
43+
})
44+
45+
it('shows API key creation modal when clicking the API key button', () => {
46+
cy.findByRole('button', { name: /API key/i }).click()
47+
48+
cy.findByTestId('modal-window')
49+
.should('be.visible')
50+
.within(() => {
51+
cy.findByText(`Create a new API key for ${gatewayId}`).should('be.visible')
52+
cy.findByLabelText('Name').should('be.visible')
53+
cy.findByLabelText('Expiry date').should('be.visible')
54+
cy.findByText('Rights').should('be.visible')
55+
cy.findByText('Gateway connection (also LNS Key)').should('be.visible')
56+
cy.findByText('CUPS Key').should('be.visible')
57+
cy.findByText('Get gateway statistics').scrollIntoView()
58+
cy.findByText('Get gateway statistics').should('be.visible')
59+
60+
cy.findByRole('button', { name: /Create API key/ }).should('be.disabled')
61+
})
62+
})
63+
64+
it('enables API key creation button when valid data is entered', () => {
65+
cy.findByRole('button', { name: /API key/i }).click()
66+
67+
cy.findByTestId('modal-window')
68+
.should('be.visible')
69+
.within(() => {
70+
cy.findByLabelText('Name').type('Test Gateway API')
71+
cy.findByLabelText('Expiry date').type('2030-01-01')
72+
73+
cy.findByText('Gateway connection (also LNS Key)').should('be.visible').click()
74+
75+
cy.findByRole('button', { name: /Create API key/ }).should('not.be.disabled')
76+
})
77+
})
78+
79+
it('shows error if error happens while creating API key', () => {
80+
cy.intercept('POST', `/api/v3/gateways/test-gateway/api-keys`, {
81+
statusCode: 500,
82+
}).as('create-api-key-error')
83+
cy.findByRole('button', { name: /API key/i }).click()
84+
85+
cy.findByTestId('modal-window')
86+
.should('be.visible')
87+
.within(() => {
88+
cy.findByText('Gateway connection (also LNS Key)').should('be.visible').click()
89+
90+
cy.findByRole('button', { name: /Create API key/ }).click()
91+
cy.wait('@create-api-key-error')
92+
})
93+
cy.findByTestId('toast-notification-error')
94+
.should('be.visible')
95+
.findByText('Failed to create API key')
96+
.should('be.visible')
97+
})
98+
99+
it('creates API key and shows confirmation modal', () => {
100+
cy.window().then(win => {
101+
cy.stub(win.navigator.clipboard, 'writeText').as('clipboardWrite')
102+
})
103+
104+
cy.findByRole('button', { name: /API key/i }).click()
105+
106+
cy.findByTestId('modal-window')
107+
.should('be.visible')
108+
.within(() => {
109+
cy.findByLabelText('Name').type('Copy Test API Key')
110+
cy.findByLabelText('Expiry date').type('2030-12-31')
111+
cy.findByText('Gateway connection (also LNS Key)').should('be.visible').click()
112+
113+
cy.findByRole('button', { name: /Create API key/ }).click()
114+
cy.wait('@create-api-key')
115+
116+
cy.findByText('Your API key has been created successfully').should('be.visible')
117+
cy.findByText(/After closing this window, the value of the key secret/i).should(
118+
'be.visible',
119+
)
120+
cy.findByText('API key')
121+
.parent()
122+
.within(() => {
123+
cy.get('div').eq(1).should('contain.text', '••••')
124+
125+
cy.get('button[title="Toggle visibility"]').click()
126+
127+
cy.get('div')
128+
.eq(1)
129+
.invoke('text')
130+
.should('not.contain', '•')
131+
.and('contain', 'GTWAPIKEY')
132+
})
133+
134+
cy.findByRole('button', { name: /Copy/ }).click()
135+
cy.get('@clipboardWrite').should('have.been.calledOnce')
136+
cy.findByRole('button', { name: /Copied to clipboard/ }).should('be.visible')
137+
138+
cy.findByRole('button', { name: /Download/ }).should('be.visible')
139+
cy.findByRole('button', { name: /I have copied the key/ }).click()
140+
})
141+
142+
cy.findByTestId('modal-window').should('not.exist')
143+
})
144+
145+
it('creates API key and downloads it to a file', () => {
146+
cy.window().then(win => {
147+
cy.spy(win.document.body, 'appendChild').as('appendChildSpy')
148+
})
149+
150+
cy.findByRole('button', { name: /API key/i }).click()
151+
152+
cy.findByTestId('modal-window')
153+
.should('be.visible')
154+
.within(() => {
155+
cy.findByLabelText('Name').type('Download Test API Key')
156+
cy.findByLabelText('Expiry date').type('2030-12-31')
157+
cy.findByText('Gateway connection (also LNS Key)').should('be.visible').click()
158+
159+
cy.findByRole('button', { name: /Create API key/ }).click()
160+
cy.wait('@create-api-key')
161+
162+
cy.findByText('Your API key has been created successfully').should('be.visible')
163+
cy.findByText(/After closing this window, the value of the key secret/i).should(
164+
'be.visible',
165+
)
166+
167+
cy.findByRole('button', { name: /Download/ }).click()
168+
cy.get('@appendChildSpy').should('have.been.called')
169+
cy.findByRole('button', { name: /I have copied the key/ }).click()
170+
})
171+
172+
cy.findByTestId('modal-window').should('not.exist')
173+
})
174+
})

pkg/webui/console/containers/api-key-modal-form/create.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,28 @@ const CreateForm = ({ entity, entityId, handleCancel, rights, setApiKey }) => {
102102
validationSchema={validationSchema}
103103
initialValues={initialValues}
104104
>
105-
<FormField
106-
title={sharedMessages.name}
107-
placeholder={sharedMessages.apiKeyNamePlaceholder}
108-
name="name"
109-
autoFocus
110-
component={Input}
111-
fieldWidth="full"
112-
/>
113-
<FormField
114-
title={'Expiry date'}
115-
name="expires_at"
116-
type="date"
117-
encode={encodeExpiryDate}
118-
decode={decodeExpiryDate}
119-
component={Input}
120-
fieldWidth="full"
121-
/>
105+
<div className="grid gap-cs-m md-lg:gap-0">
106+
<FormField
107+
title={sharedMessages.name}
108+
placeholder={sharedMessages.apiKeyNamePlaceholder}
109+
name="name"
110+
className="item-12 md-lg:item-6"
111+
autoFocus
112+
component={Input}
113+
fieldWidth="full"
114+
/>
115+
<FormField
116+
title={'Expiry date'}
117+
name="expires_at"
118+
type="date"
119+
className="item-12 md-lg:item-6"
120+
encode={encodeExpiryDate}
121+
decode={decodeExpiryDate}
122+
component={Input}
123+
fieldWidth="full"
124+
/>
125+
</div>
126+
122127
<FormField
123128
name="rights"
124129
title={sharedMessages.rights}

pkg/webui/console/containers/create-api-key-modal/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,14 @@ const CreateApiKeyModal = ({ entityId, entityName, modalVisible, setModalVisible
8888
try {
8989
const apiKeyDataUri = composeDataUri(apiKey, 'data:text/plain;charset=utf-8')
9090
downloadDataUriAsFile(apiKeyDataUri, `${entityId}_api_key_${Date.now()}.txt`)
91-
handleCloseModal()
9291
} catch (err) {
9392
toast({
9493
title: m.apiKeyFileFailed,
9594
message: m.apiKeyFileFailedMessage,
9695
type: toast.types.ERROR,
9796
})
9897
}
99-
}, [apiKey, entityId, handleCloseModal])
98+
}, [apiKey, entityId])
10099

101100
const firstStep = (
102101
<div className="flex-column gap-cs-xl w-full">

0 commit comments

Comments
 (0)