Skip to content

Commit 97b90a6

Browse files
authored
Merge branch 'master' into refactor/29914-error-redesign-v2
2 parents f23f141 + 877c5c2 commit 97b90a6

39 files changed

+1061
-137
lines changed

hivemq-edge-frontend/cypress/e2e/Login/login.spec.cy.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { mockAuthApi, mockValidCredentials } from '@/api/hooks/usePostAuthentication/__handlers__'
22
import { mockGatewayConfiguration } from '@/api/hooks/useFrontendServices/__handlers__'
3-
import { loginPage } from '../../pages/Login/LoginPage.ts'
3+
import { loginPage } from 'cypress/pages'
44

55
describe('Login Page', () => {
66
beforeEach(() => {
7-
cy.intercept('/api/v1/frontend/notifications', { statusCode: 404 })
7+
cy.intercept('/api/v1/frontend/capabilities', { statusCode: 203, log: false })
8+
cy.intercept('/api/v1/frontend/notifications', { statusCode: 203, log: false })
89
cy.intercept('/api/v1/auth/authenticate', mockAuthApi(mockValidCredentials))
910
cy.intercept('/api/v1/frontend/configuration', {
1011
...mockGatewayConfiguration,

hivemq-edge-frontend/cypress/e2e/adapters/s7.spec.cy.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('S7 adapter', () => {
1212
cy_interceptCoreE2E()
1313

1414
// TODO[E2E] This is the mock for the S7 protocol adapter
15-
cy.intercept('/api/v1/management/protocol-adapters/types', { items: [MOCK_PROTOCOL_S7] }).as('getProtocols')
15+
cy.intercept('/api/v1/management/protocol-adapters/types', { items: [MOCK_PROTOCOL_S7] })
1616

1717
// TODO[E2E] This doesn't work: JWT needs mocking
1818
loginPage.visit('/app/protocol-adapters/catalog/new/s7')
@@ -176,6 +176,8 @@ describe('S7 adapter', () => {
176176
})
177177

178178
it('should handle error', () => {
179+
cy.intercept('/api/v1/management/protocol-adapters/adapters/**', { statusCode: 203, log: false })
180+
179181
adapterPage.config.submitButton.click()
180182

181183
adapterPage.config.errorSummary.should('have.length', 2)
@@ -224,7 +226,21 @@ describe('S7 adapter', () => {
224226
beforeEach(() => {
225227
const mockResponse: DomainTagList = { items: MOCK_DEVICE_TAGS('s7-1', MockAdapterType.SIMULATION) }
226228

227-
cy.intercept('/api/v1/management/protocol-adapters/adapters/*/tags', mockResponse).as('tags')
229+
cy.intercept('/api/v1/management/bridges', { statusCode: 202, log: false })
230+
cy.intercept('/api/v1/gateway/listeners', { statusCode: 202, log: false })
231+
cy.intercept('/api/v1/management/combiners', { statusCode: 202, log: false })
232+
cy.intercept('/api/v1/management/topic-filters', { statusCode: 202, log: false })
233+
cy.intercept('/api/v1/management/protocol-adapters/adapters/**/northboundMappings', {
234+
statusCode: 202,
235+
log: false,
236+
})
237+
cy.intercept('/api/v1/management/protocol-adapters/adapters/**/southboundMappings', {
238+
statusCode: 202,
239+
log: false,
240+
})
241+
cy.intercept('/api/v1/data-hub/data-validation/policies', { statusCode: 202, log: false })
242+
243+
cy.intercept('/api/v1/management/protocol-adapters/adapters/*/tags', mockResponse)
228244
cy.intercept('/api/v1/management/protocol-adapters/tag-schemas/s7', MOCK_SCHEMA_S7)
229245

230246
cy.intercept('/api/v1/management/protocol-adapters/adapters', { items: [MOCK_ADAPTER_S7] }).as('getAdapters')

hivemq-edge-frontend/cypress/e2e/bridges/bridges.spec.cy.ts

Lines changed: 57 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { drop, factory, primaryKey } from '@mswjs/data'
22

3-
import type { Bridge } from '@/api/__generated__'
4-
import { cy_interceptCoreE2E } from 'cypress/utils/intercept.utils.ts'
5-
import { bridgePage, loginPage, rjsf } from 'cypress/pages'
3+
import { cy_interceptCoreE2E, cy_interceptWithMockDB } from 'cypress/utils/intercept.utils.ts'
4+
import { bridgePage, loginPage, rjsf, workspacePage } from 'cypress/pages'
5+
import { workspaceBridgePanel } from '../../pages/Workspace/BridgeFormPage.ts'
6+
import { MOCK_TOPIC_FILTER } from '@/api/hooks/useTopicFilters/__handlers__'
67

78
describe('Bridges', () => {
89
// Creating a mock storage for the Bridges
@@ -18,51 +19,7 @@ describe('Bridges', () => {
1819

1920
cy_interceptCoreE2E()
2021

21-
// TODO[NVL] Add support for the Event Log
22-
cy.intercept('GET', '/api/v1/management/bridges', (req) => {
23-
const allBridgeData = mswDB.bridge.getAll()
24-
const allBridges = allBridgeData.map<Bridge>((data) => ({ ...JSON.parse(data.json) }))
25-
req.reply(200, { items: allBridges })
26-
})
27-
28-
cy.intercept<Bridge>('POST', '/api/v1/management/bridges', (req) => {
29-
const bridge = req.body
30-
const newBridgeData = mswDB.bridge.create({
31-
id: bridge.id,
32-
json: JSON.stringify(bridge),
33-
})
34-
req.reply(200, newBridgeData)
35-
})
36-
37-
cy.intercept<Bridge>('PUT', '/api/v1/management/bridges/**', (req) => {
38-
const bridge = req.body
39-
40-
mswDB.bridge.update({
41-
where: {
42-
id: {
43-
equals: bridge.id,
44-
},
45-
},
46-
47-
data: { json: JSON.stringify(bridge) },
48-
})
49-
50-
req.reply(200, '')
51-
})
52-
53-
cy.intercept<Bridge>('DELETE', '/api/v1/management/bridges/**', (req) => {
54-
const urlParts = req.url.split('/')
55-
const bridgeId = urlParts[urlParts.length - 1]
56-
57-
mswDB.bridge.delete({
58-
where: {
59-
id: {
60-
equals: bridgeId,
61-
},
62-
},
63-
})
64-
req.reply(200, '')
65-
}).as('deleteBridge')
22+
cy_interceptWithMockDB(mswDB)
6623

6724
loginPage.visit('/app/mqtt-bridges')
6825
loginPage.loginButton.click()
@@ -172,9 +129,34 @@ describe('Bridges', () => {
172129
})
173130

174131
context('Bridge in Workspace', () => {
132+
beforeEach(() => {
133+
cy.intercept('/api/v1/management/protocol-adapters/types', { statusCode: 202, log: false })
134+
cy.intercept('/api/v1/gateway/listeners', { statusCode: 202, log: false })
135+
cy.intercept('/api/v1/management/combiners', { statusCode: 202, log: false })
136+
cy.intercept('/api/v1/management/protocol-adapters/adapters/**/northboundMappings', {
137+
statusCode: 202,
138+
log: false,
139+
})
140+
cy.intercept('/api/v1/management/protocol-adapters/adapters/**/southboundMappings', {
141+
statusCode: 202,
142+
log: false,
143+
})
144+
cy.intercept('/api/v1/management/protocol-adapters/adapters/**/tags', { statusCode: 202, log: false })
145+
cy.intercept('/api/v1/data-hub/data-validation/policies', { statusCode: 202, log: false })
146+
cy.intercept('/api/v1/management/topic-filters', {
147+
items: [MOCK_TOPIC_FILTER],
148+
})
149+
})
150+
175151
it('should create a bridge also in the Workspace', () => {
176152
bridgePage.table.status.should('have.text', 'No bridges currently created')
177153

154+
workspacePage.navLink.click()
155+
workspacePage.edgeNode.should('contain.text', 'HiveMQ Edge')
156+
workspacePage.bridgeNodes().should('have.length', 0)
157+
158+
bridgePage.navLink.click()
159+
178160
bridgePage.addNewBridge.click()
179161

180162
bridgePage.config.panel.should('be.visible')
@@ -186,13 +168,37 @@ describe('Bridges', () => {
186168
rjsf.field('clientId').input.type('my-client-id')
187169

188170
bridgePage.config.submitButton.click()
171+
bridgePage.toast.close()
172+
173+
workspacePage.navLink.click()
174+
workspacePage.toolbox.fit.click()
175+
// Needed because the fit reduce the visual content of the nodes
176+
workspacePage.toolbox.zoomIn.click().click()
177+
workspacePage.bridgeNode('my-bridge').should('be.visible')
178+
workspacePage.bridgeNode('my-bridge').within(() => {
179+
cy.getByTestId('connection-status').should('have.text', 'Connected')
180+
})
181+
workspacePage.bridgeNode('my-bridge').click()
182+
workspacePage.toolbar.overview.click()
189183

190-
//TODO[NVL] Better create a subscription
184+
workspaceBridgePanel.form.should('be.visible')
185+
workspaceBridgePanel.modifyBridge.click()
186+
workspaceBridgePanel.form.should('not.exist')
187+
188+
bridgePage.config.panel.should('be.visible')
189+
bridgePage.config.formTab(2).click()
190+
191+
// This will be item #0
192+
rjsf.field('localSubscriptions').addItem.click()
193+
rjsf.field(['localSubscriptions', '0', 'destination']).input.type('my/topic')
194+
195+
rjsf.field(['localSubscriptions', '0', 'filters']).addItem.click()
196+
rjsf.field(['localSubscriptions', '0', 'filters', '0']).input.type('first/filter')
197+
198+
bridgePage.config.submitButton.click()
191199
})
192200
})
193201

194-
context('Bridge in Event Log', () => {})
195-
196202
it('should be accessible', () => {
197203
cy.injectAxe()
198204
bridgePage.table.status.should('have.text', 'No bridges currently created')
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { drop, factory, primaryKey } from '@mswjs/data'
2+
import { DateTime } from 'luxon'
3+
4+
import mockDataPolicy from 'cypress/fixtures/test-2025-07-23.json'
5+
import mockSchemaValidator from 'cypress/fixtures/test-schema-2025-07-23.json'
6+
import mockSchemaDeserialise from 'cypress/fixtures/test-deserialise-2025-07-23.json'
7+
import mockSchemaSerialise from 'cypress/fixtures/test-serialise-2025-07-23.json'
8+
import mockFunction from 'cypress/fixtures/test-function-2025-07-23.json'
9+
10+
import type { DataHubFactory } from 'cypress/utils/intercept.utils.ts'
11+
import { cy_interceptCoreE2E, cy_interceptDataHubWithMockDB } from 'cypress/utils/intercept.utils.ts'
12+
import { datahubPage, loginPage } from 'cypress/pages'
13+
import { datahubDesignerPage } from 'cypress/pages/DataHub/DesignerPage.ts'
14+
import { cy_checkDataPolicyGraph } from 'cypress/utils/datahub.utils.ts'
15+
16+
import { MOCK_CAPABILITIES } from '@/api/hooks/useFrontendServices/__handlers__'
17+
import { MOCK_DATAHUB_FUNCTIONS } from '@datahub/api/hooks/DataHubFunctionsService/__handlers__'
18+
19+
describe('Data Hub', () => {
20+
// Creating a mock storage for the Data Hub
21+
const mswDB: DataHubFactory = factory({
22+
dataPolicy: {
23+
id: primaryKey(String),
24+
json: String,
25+
},
26+
behaviourPolicy: {
27+
id: primaryKey(String),
28+
json: String,
29+
},
30+
schema: {
31+
id: primaryKey(String),
32+
json: String,
33+
},
34+
script: {
35+
id: primaryKey(String),
36+
json: String,
37+
},
38+
})
39+
40+
beforeEach(() => {
41+
drop(mswDB)
42+
43+
cy_interceptCoreE2E()
44+
cy.intercept('/api/v1/frontend/capabilities', MOCK_CAPABILITIES)
45+
cy.intercept('/api/v1/data-hub/function-specs', {
46+
items: MOCK_DATAHUB_FUNCTIONS.items.map((specs) => {
47+
specs.metadata.inLicenseAllowed = true
48+
return specs
49+
}),
50+
})
51+
cy_interceptDataHubWithMockDB(mswDB)
52+
53+
loginPage.visit('/app/datahub')
54+
loginPage.loginButton.click()
55+
datahubPage.navLink.click()
56+
})
57+
58+
it('should render properly', () => {
59+
datahubPage.pageHeader.should('have.text', 'Data Hub on Edge')
60+
datahubPage.policiesTable.status.should('have.text', 'No data received yet.')
61+
62+
datahubPage.addNewPolicy.click()
63+
64+
datahubDesignerPage.toolbox.trigger.click()
65+
66+
datahubDesignerPage.toolbox.dataPolicy.drag('[role="region"][data-testid="rf__wrapper"]')
67+
datahubDesignerPage.controls.fit.click()
68+
69+
datahubDesignerPage.toolbox.trigger.click()
70+
datahubDesignerPage.toolbox.topicFilter.drag('[role="region"][data-testid="rf__wrapper"]')
71+
datahubDesignerPage.controls.fit.click()
72+
73+
datahubDesignerPage.designer.connectNodes('TOPIC_FILTER', 'topic-0', 'DATA_POLICY', 'topicFilter')
74+
datahubDesignerPage.controls.fit.click()
75+
76+
datahubDesignerPage.designer.createOnDrop('DATA_POLICY', 'onSuccess')
77+
datahubDesignerPage.controls.fit.click()
78+
79+
datahubDesignerPage.designer.createOnDrop('DATA_POLICY', 'onError')
80+
datahubDesignerPage.controls.fit.click()
81+
82+
// TODO[NVL] Cannot make the .move works here. Need to investigate
83+
datahubDesignerPage.designer.mode('TOPIC_FILTER').type(datahubDesignerPage.leftArrows)
84+
})
85+
86+
describe('Data Hub', () => {
87+
beforeEach(() => {
88+
const dateNow = Date.now()
89+
const formattedDate = DateTime.fromMillis(dateNow).minus({ minutes: 20 }).toISO({ format: 'basic' }) as string
90+
91+
// Load the mock data policy into the mock databases
92+
mswDB.dataPolicy.create({
93+
id: mockDataPolicy.id,
94+
json: JSON.stringify({ ...mockDataPolicy, createdAt: formattedDate, lastUpdatedAt: formattedDate }),
95+
})
96+
mswDB.schema.create({
97+
id: mockSchemaValidator.id,
98+
json: JSON.stringify({ ...mockSchemaValidator, createdAt: formattedDate }),
99+
})
100+
mswDB.schema.create({
101+
id: mockSchemaDeserialise.id,
102+
json: JSON.stringify({ ...mockSchemaDeserialise, createdAt: formattedDate }),
103+
})
104+
mswDB.schema.create({
105+
id: mockSchemaSerialise.id,
106+
json: JSON.stringify({ ...mockSchemaSerialise, createdAt: formattedDate }),
107+
})
108+
mswDB.script.create({
109+
id: mockFunction.id,
110+
json: JSON.stringify({ ...mockFunction, createdAt: formattedDate }),
111+
})
112+
})
113+
114+
it('should load and render a data policy', () => {
115+
datahubPage.pageHeader.should('have.text', 'Data Hub on Edge')
116+
datahubPage.policiesTable.rows.should('have.length', 1)
117+
118+
datahubPage.policiesTable.cell(0, 'id').should('have.text', 'test')
119+
datahubPage.policiesTable.cell(0, 'type').should('have.text', 'Data Policy')
120+
datahubPage.policiesTable.cell(0, 'matching').should('have.text', 'topic/example/1')
121+
datahubPage.policiesTable.cell(0, 'created').should('have.text', '20 minutes ago')
122+
datahubPage.policiesTable.action(0, 'edit').click()
123+
124+
cy.url().should('contain', '/datahub/DATA_POLICY/test')
125+
126+
datahubDesignerPage.designer.modes().then(($elements) => {
127+
const dataIds = $elements.toArray().map((el) => el.getAttribute('data-id'))
128+
129+
expect(dataIds.length).to.equal(8)
130+
// IDs must be unique
131+
expect(new Set(dataIds).size).to.equal(dataIds.length)
132+
cy.wrap(dataIds).as('nodeIds')
133+
})
134+
135+
datahubDesignerPage.designer.edges().then(($elements) => {
136+
const dataIds = $elements.toArray().map((el) => el.getAttribute('data-id'))
137+
138+
expect(dataIds.length).to.equal(7)
139+
140+
cy.wrap(dataIds).as('edgeIds')
141+
})
142+
143+
cy_checkDataPolicyGraph()
144+
})
145+
})
146+
})

0 commit comments

Comments
 (0)