From 3b1cadb9166d71d98d4093d57108c4607c17da32 Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Mon, 22 Jun 2026 16:25:17 +0200 Subject: [PATCH 1/3] fix(settings): correct heading order in account management sidebar Signed-off-by: Peter Ringelmann --- .../components/AppNavigationGroupList.spec.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 apps/settings/src/components/AppNavigationGroupList.spec.ts diff --git a/apps/settings/src/components/AppNavigationGroupList.spec.ts b/apps/settings/src/components/AppNavigationGroupList.spec.ts new file mode 100644 index 0000000000000..01b53caf27bb3 --- /dev/null +++ b/apps/settings/src/components/AppNavigationGroupList.spec.ts @@ -0,0 +1,57 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { mount } from '@vue/test-utils' +import { ref } from 'vue' +import { describe, expect, it, vi } from 'vitest' +import NcAppNavigationCaption from '@nextcloud/vue/components/NcAppNavigationCaption' + +// The component builds a real Vuex store via useStore(); mock it so this stays +// a focused component test that controls its own data. +vi.mock('../store/index.js', () => ({ + useStore: () => ({ + getters: { + getServerData: { isAdmin: false, isDelegatedAdmin: false }, + getSortedGroups: [], + getSubAdminGroups: [], + getSearchQuery: '', + }, + commit: vi.fn(), + dispatch: vi.fn(), + }), +})) + +vi.mock('vue-router/composables', async (importActual) => ({ + ...(await importActual()), + useRoute: () => ({ params: {} }), + useRouter: () => ({ push: vi.fn() }), +})) + +vi.mock('../service/groups.ts', () => ({ + searchGroups: () => Promise.resolve([]), +})) + +vi.mock('@vueuse/core', async (importActual) => ({ + ...(await importActual()), + useElementVisibility: () => ref(false), +})) + +import AppNavigationGroupList from './AppNavigationGroupList.vue' + +describe('AppNavigationGroupList', () => { + it('does not expose the group list as a heading (BITV 9.1.3.1a)', () => { + const wrapper = mount(AppNavigationGroupList) + + // The sidebar group list is navigation, not document structure. It must + // not emit a heading, which would sit before the page

in the DOM + // and produce an out-of-order outline (h2 before h1). + const caption = wrapper.findComponent(NcAppNavigationCaption) + expect(caption.exists()).toBe(true) + expect(caption.find('h1,h2,h3,h4,h5,h6').exists()).toBe(false) + + // The "Groups" label is still rendered, just not as a heading. + expect(caption.text()).toContain('Groups') + }) +}) From dc099317b90a344bf8ec72a24ef4da13c80d7e1c Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Tue, 23 Jun 2026 13:42:43 +0200 Subject: [PATCH 2/3] test(settings): exclude the group caption from group-row selectors test(settings): exclude the group caption from group-row selectors Signed-off-by: Peter Ringelmann [skip ci] --- cypress/e2e/settings/users_groups.cy.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cypress/e2e/settings/users_groups.cy.ts b/cypress/e2e/settings/users_groups.cy.ts index 8d84ddc6bb4e9..4176e81eee962 100644 --- a/cypress/e2e/settings/users_groups.cy.ts +++ b/cypress/e2e/settings/users_groups.cy.ts @@ -248,16 +248,16 @@ describe('Settings: Sort groups in the UI', () => { it('See that the groups are sorted by the member count', () => { cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => { - cy.get('li').eq(0).should('contain', 'B') // 1 member - cy.get('li').eq(1).should('contain', 'A') // 0 members + cy.get('li').not('.app-navigation-caption').eq(0).should('contain', 'B') // 1 member + cy.get('li').not('.app-navigation-caption').eq(1).should('contain', 'A') // 0 members }) }) it('See that the order is preserved after a reload', () => { cy.reload() cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => { - cy.get('li').eq(0).should('contain', 'B') // 1 member - cy.get('li').eq(1).should('contain', 'A') // 0 members + cy.get('li').not('.app-navigation-caption').eq(0).should('contain', 'B') // 1 member + cy.get('li').not('.app-navigation-caption').eq(1).should('contain', 'A') // 0 members }) }) @@ -276,16 +276,16 @@ describe('Settings: Sort groups in the UI', () => { it('See that the groups are sorted by the user count', () => { cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => { - cy.get('li').eq(0).should('contain', 'A') - cy.get('li').eq(1).should('contain', 'B') + cy.get('li').not('.app-navigation-caption').eq(0).should('contain', 'A') + cy.get('li').not('.app-navigation-caption').eq(1).should('contain', 'B') }) }) it('See that the order is preserved after a reload', () => { cy.reload() cy.get('ul[data-cy-users-settings-navigation-groups="custom"]').within(() => { - cy.get('li').eq(0).should('contain', 'A') - cy.get('li').eq(1).should('contain', 'B') + cy.get('li').not('.app-navigation-caption').eq(0).should('contain', 'A') + cy.get('li').not('.app-navigation-caption').eq(1).should('contain', 'B') }) }) }) From 37c8362bf46ac827636259c69e3a0e8f8148a661 Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Tue, 23 Jun 2026 18:16:31 +0200 Subject: [PATCH 3/3] fix(settings): correct heading order in account management sidebar Signed-off-by: Peter Ringelmann --- apps/settings/src/components/AppNavigationGroupList.spec.ts | 3 +-- apps/settings/src/components/AppNavigationGroupList.vue | 2 +- dist/settings-users-3239.js | 4 ++-- dist/settings-users-3239.js.map | 2 +- dist/settings-vue-settings-apps-users-management.js | 4 ++-- dist/settings-vue-settings-apps-users-management.js.map | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/settings/src/components/AppNavigationGroupList.spec.ts b/apps/settings/src/components/AppNavigationGroupList.spec.ts index 01b53caf27bb3..dfef4930579c9 100644 --- a/apps/settings/src/components/AppNavigationGroupList.spec.ts +++ b/apps/settings/src/components/AppNavigationGroupList.spec.ts @@ -7,6 +7,7 @@ import { mount } from '@vue/test-utils' import { ref } from 'vue' import { describe, expect, it, vi } from 'vitest' import NcAppNavigationCaption from '@nextcloud/vue/components/NcAppNavigationCaption' +import AppNavigationGroupList from './AppNavigationGroupList.vue' // The component builds a real Vuex store via useStore(); mock it so this stays // a focused component test that controls its own data. @@ -38,8 +39,6 @@ vi.mock('@vueuse/core', async (importActual) => ({ useElementVisibility: () => ref(false), })) -import AppNavigationGroupList from './AppNavigationGroupList.vue' - describe('AppNavigationGroupList', () => { it('does not expose the group list as a heading (BITV 9.1.3.1a)', () => { const wrapper = mount(AppNavigationGroupList) diff --git a/apps/settings/src/components/AppNavigationGroupList.vue b/apps/settings/src/components/AppNavigationGroupList.vue index 8f21d18d69513..72d4f24ac04c5 100644 --- a/apps/settings/src/components/AppNavigationGroupList.vue +++ b/apps/settings/src/components/AppNavigationGroupList.vue @@ -9,7 +9,6 @@ :disabled="loadingAddGroup" :aria-label="loadingAddGroup ? t('settings', 'Creating group…') : t('settings', 'Create group')" force-menu - is-heading :open.sync="isAddGroupOpen">