Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions cypress/e2e/console/devices/mac-settings-profiles/assign.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright © 2025 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

describe('Device MAC settings profile assignment', () => {
const userId = 'device-mac-profile-test-user'
const user = {
ids: { user_id: userId },
primary_email_address: 'device-mac-profile-test-user@example.com',
password: 'ABCDefg123!',
password_confirm: 'ABCDefg123!',
}

const applicationId = 'app-device-mac-profile-test'
const application = {
ids: { application_id: applicationId },
name: 'Application for device MAC profile test',
}

const macSettingsProfiles = [
{
ids: {
application_ids: { application_id: applicationId },
profile_id: 'compatible-profile',
},
mac_settings: {
supports_32_bit_f_cnt: true,
rx1_delay: 1,
},
},
{
ids: {
application_ids: { application_id: applicationId },
profile_id: 'another-profile',
},
mac_settings: {
supports_32_bit_f_cnt: false,
rx1_delay: 5,
},
},
]

const ns = {
end_device: {
frequency_plan_id: 'EU_863_870_TTN',
lorawan_phy_version: 'PHY_V1_0_2_REV_B',
multicast: false,
supports_join: false,
lorawan_version: 'MAC_V1_0_2',
session: {
keys: {
f_nwk_s_int_key: {
key: 'CBFBF585D81A9063A31EA6922EDD6360',
},
},
dev_addr: '270000FC',
},
supports_class_c: false,
supports_class_b: false,
mac_settings: {
rx2_data_rate_index: 0,
rx2_frequency: 869525000,
rx1_delay: 1,
rx1_data_rate_offset: 0,
resets_f_cnt: false,
},
},
}

let endDeviceId

before(() => {
cy.dropAndSeedDatabase()
cy.createUser(user)
cy.createApplication(application, userId)
cy.createMockDeviceAllComponents(applicationId, undefined, { ns }).then(body => {
endDeviceId = body.end_device.ids.device_id
})
macSettingsProfiles.forEach(profile => cy.createMacSettingsProfile(applicationId, profile))
})

beforeEach(() => {
cy.loginConsole({ user_id: user.ids.user_id, password: user.password })
cy.visit(
`${Cypress.config('consoleRootPath')}/applications/${applicationId}/devices/${endDeviceId}/general-settings`,
)
})

it('displays MAC settings profile selection in device edit form', () => {
cy.get('button[type="submit"]').scrollIntoView()
cy.findByText('Network layer')
.should('be.visible')
.closest('[data-test-id="collapsible-section"]')
.within(() => {
cy.findByRole('button', { name: 'Expand' }).click()
cy.get('button[type="submit"]').scrollIntoView()
cy.findByLabelText('MAC settings profile').should('be.visible')
cy.findByRole('button', { name: /Remove/ }).should('be.disabled')
})
})

it('succeeds to assign MAC settings profile to existing device', () => {
cy.get('button[type="submit"]').scrollIntoView()
cy.findByText('Network layer')
.should('be.visible')
.closest('[data-test-id="collapsible-section"]')
.within(() => {
cy.findByRole('button', { name: 'Expand' }).click()
cy.get('button[type="submit"]').scrollIntoView()
cy.findByLabelText('MAC settings profile').selectOption('compatible-profile')
cy.findByRole('button', { name: /Remove/ }).should('not.be.disabled')
cy.findByRole('button', { name: /Save changes/ }).click()
})

cy.findByTestId('toast-notification-success')
.should('be.visible')
.and('contain', 'End device updated')
})

it('succeeds to remove MAC settings profile from device', () => {
cy.get('button[type="submit"]').scrollIntoView()
cy.findByText('Network layer')
.should('be.visible')
.closest('[data-test-id="collapsible-section"]')
.within(() => {
cy.findByRole('button', { name: 'Expand' }).click()
cy.get('button[type="submit"]').scrollIntoView()
cy.findByLabelText('MAC settings profile').selectOption('compatible-profile')
cy.findByRole('button', { name: /Remove/ }).click()
cy.findByLabelText('MAC settings profile').should('have.value', '')
cy.findByRole('button', { name: /Save changes/ }).click()
})

cy.findByTestId('toast-notification-success')
.should('be.visible')
.and('contain', 'End device updated')
})

it('shows custom MAC settings when no profile is selected', () => {
cy.get('button[type="submit"]').scrollIntoView()
cy.findByText('Network layer')
.should('be.visible')
.closest('[data-test-id="collapsible-section"]')
.within(() => {
cy.findByRole('button', { name: 'Expand' }).click()
cy.get('button[type="submit"]').scrollIntoView()
cy.findByLabelText('MAC settings profile').should('have.value', '')
cy.findByRole('button', { name: /Remove/ }).should('be.disabled')
cy.findByText('Custom MAC settings').click()
cy.get('#mac-settings').scrollIntoView()
cy.findByText('Frame counter width').should('be.visible')
})
})

it('hides custom MAC settings when profile is selected', () => {
cy.get('button[type="submit"]').scrollIntoView()
cy.findByText('Network layer')
.should('be.visible')
.closest('[data-test-id="collapsible-section"]')
.within(() => {
cy.findByRole('button', { name: 'Expand' }).click()
cy.get('button[type="submit"]').scrollIntoView()
cy.findByLabelText('MAC settings profile').selectOption('compatible-profile')
cy.findByText('Custom MAC settings').should('not.exist')
})
})

it('navigates to create MAC settings profile when clicking create button', () => {
// Mock empty profiles to show the create button
cy.intercept('GET', `/api/v3/ns/applications/${applicationId}/mac_settings_profiles*`, {
statusCode: 200,
body: {
mac_settings_profiles: [],
},
}).as('getEmptyProfiles')

cy.reload()
cy.wait('@getEmptyProfiles')

cy.get('button[type="submit"]').scrollIntoView()
cy.findByText('Network layer')
.should('be.visible')
.closest('[data-test-id="collapsible-section"]')
.within(() => {
cy.findByRole('button', { name: 'Expand' }).click()
cy.get('button[type="submit"]').scrollIntoView()
cy.findByRole('button', { name: /Create a new MAC settings profile/ }).click()
})

cy.location('pathname').should(
'eq',
`${Cypress.config('consoleRootPath')}/applications/${applicationId}/mac-settings-profiles/add`,
)
})
})
153 changes: 153 additions & 0 deletions cypress/e2e/console/devices/mac-settings-profiles/create.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright © 2025 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

describe('MAC settings profile creation', () => {
const userId = 'mac-settings-prof-create-test-user'
const user = {
ids: { user_id: userId },
primary_email_address: 'mac-settings-profile-create-test-user@example.com',
password: 'ABCDefg123!',
password_confirm: 'ABCDefg123!',
}

const applicationId = 'app-mac-settings-prof-create-test'
const application = {
ids: { application_id: applicationId },
name: 'Application for MAC settings profile creation test',
}

before(() => {
cy.dropAndSeedDatabase()
cy.createUser(user)
cy.createApplication(application, userId)
})

beforeEach(() => {
cy.loginConsole({ user_id: user.ids.user_id, password: user.password })

cy.visit(
`${Cypress.config('consoleRootPath')}/applications/${applicationId}/mac-settings-profiles/add`,
)
})

it('displays MAC settings profile creation form', () => {
cy.findByText('Create a new MAC settings profile', { selector: 'h1' }).should('be.visible')
cy.findByLabelText('Profile ID').should('be.visible')
cy.findByText('Frame counter width').should('be.visible')
cy.findByLabelText('32 bit').should('be.checked')
cy.findByLabelText('Rx1 delay').should('be.visible')
cy.findByLabelText('Desired Rx1 delay').should('be.visible')
cy.findByLabelText('Rx1 data rate offset').should('be.visible')
cy.findByLabelText('Desired Rx1 data rate offset').should('be.visible')
cy.findByLabelText('Rx2 data rate index').should('be.visible')
cy.findByLabelText('Desired Rx2 data rate index').should('be.visible')
cy.findByLabelText('Rx2 frequency').should('be.visible')
cy.findByLabelText('Desired Rx2 frequency').should('be.visible')
cy.findByLabelText('Maximum duty cycle').should('be.visible')
cy.findByLabelText('Desired maximum duty cycle').should('be.visible')
cy.findByText('Factory preset frequencies').should('be.visible')
cy.findByText('Class C timeout').should('be.visible')
cy.get('input[name="mac_settings.beacon_frequency"]').scrollIntoView()
cy.findByLabelText('Ping slot periodicity').should('be.visible')
cy.findByLabelText('Beacon frequency').should('be.visible')
cy.findByLabelText('Desired beacon frequency').should('be.visible')
cy.findByLabelText('Ping slot frequency').should('be.visible')
cy.findByLabelText('Desired ping slot frequency').should('be.visible')
cy.findByLabelText('Ping slot data rate index').should('be.visible')
cy.findByLabelText('Desired ping slot data rate').should('be.visible')
cy.findByLabelText('Status count periodicity').should('be.visible')
cy.findByLabelText('Status time periodicity').should('be.visible')
cy.findByText('Adaptive data rate (ADR)').should('be.visible')
})

it('validates required fields', () => {
cy.findByRole('button', { name: /Create a new MAC settings profile/ }).click()
cy.findErrorByLabelText('Profile ID')
.should('contain.text', 'Profile ID is required')
.and('be.visible')
})

it('succeeds to create basic MAC settings profile', () => {
const profileData = {
profile_id: 'basic-test-profile',
rx1_delay: '2',
rx2_data_rate_index: '3',
}

cy.findByLabelText('Profile ID').type(profileData.profile_id)
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.findByLabelText('Rx1 delay').clear().type(profileData.rx1_delay)
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.findByLabelText('Rx2 data rate index').clear().type(profileData.rx2_data_rate_index)

cy.findByRole('button', { name: /Create a new MAC settings profile/ }).click()

cy.findByTestId('toast-notification-success')
.should('be.visible')
.and('contain', 'MAC settings profile created')

cy.location('pathname').should(
'eq',
`${Cypress.config('consoleRootPath')}/applications/${applicationId}/mac-settings-profiles`,
)
})

it('succeeds to create MAC settings profile with ADR dynamic settings', () => {
const profileData = {
profile_id: 'adr-dynamic-profile',
adr_margin: '10',
min_nb_trans: '1',
max_nb_trans: '3',
}

cy.findByLabelText('Profile ID').type(profileData.profile_id)

cy.findByLabelText('ADR margin').type(profileData.adr_margin)

cy.findByLabelText('Use default settings for number of retransmissions').uncheck()
cy.findByLabelText('Override server defaults for NbTrans (all data rates)').check()

cy.findByLabelText('Min. NbTrans').type(profileData.min_nb_trans)
cy.findByLabelText('Max. NbTrans').type(profileData.max_nb_trans)

cy.findByRole('button', { name: /Create a new MAC settings profile/ }).click()

cy.findByTestId('toast-notification-success')
.should('be.visible')
.and('contain', 'MAC settings profile created')
})

it('succeeds to create MAC settings profile with static ADR settings', () => {
const profileData = {
profile_id: 'adr-static-profile',
data_rate_index: '5',
tx_power_index: '2',
nb_trans: '2',
}

cy.findByLabelText('Profile ID').type(profileData.profile_id)

// Select static ADR
cy.findByLabelText('Static mode').check()
cy.findByLabelText('ADR data rate index').type(profileData.data_rate_index)
cy.findByLabelText('ADR transmission power index').type(profileData.tx_power_index)
cy.findByLabelText('ADR number of transmissions').type(profileData.nb_trans)

cy.findByRole('button', { name: /Create a new MAC settings profile/ }).click()

cy.findByTestId('toast-notification-success')
.should('be.visible')
.and('contain', 'MAC settings profile created')
})
})
Loading
Loading