123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- /**
- * @copyright Copyright (c) 2024 Ferdinand Thiessen <opensource@fthiessen.de>
- *
- * @author Ferdinand Thiessen <opensource@fthiessen.de>
- *
- * @license AGPL-3.0-or-later
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- import type { User } from '@nextcloud/cypress'
- import { handlePasswordConfirmation } from './usersUtils.ts'
- let user: User
- enum Visibility {
- Private = 'Private',
- Local = 'Local',
- Federated = 'Federated',
- Public = 'Published'
- }
- const ALL_VISIBILITIES = [Visibility.Public, Visibility.Private, Visibility.Local, Visibility.Federated]
- /**
- * Get the input connected to a specific label
- * @param label The content of the label
- */
- const inputForLabel = (label: string) => cy.contains('label', label).then((el) => cy.get(`#${el.attr('for')}`))
- /**
- * Get the property visibility button
- * @param property The property to which to look for the button
- */
- const getVisibilityButton = (property: string) => cy.get(`button[aria-label*="Change scope level of ${property.toLowerCase()}"`)
- /**
- * Validate a specifiy visibility is set for a property
- * @param property The property
- * @param active The active visibility
- */
- const validateActiveVisibility = (property: string, active: Visibility) => {
- getVisibilityButton(property)
- .should('have.attr', 'aria-label')
- .and('match', new RegExp(`current scope is ${active}`, 'i'))
- getVisibilityButton(property)
- .click()
- cy.get('ul[role="menu"]')
- .contains('button', active)
- .should('have.attr', 'aria-checked', 'true')
- // close menu
- getVisibilityButton(property)
- .click()
- }
- /**
- * Set a specific visibility for a property
- * @param property The property
- * @param active The visibility to set
- */
- const setActiveVisibility = (property: string, active: Visibility) => {
- getVisibilityButton(property)
- .click()
- cy.get('ul[role="menu"]')
- .contains('button', active)
- .click({ force: true })
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- }
- /**
- * Helper to check that setting all visibilities on a property is possible
- * @param property The property to test
- * @param defaultVisibility The default visibility of that property
- * @param allowedVisibility Visibility that is allowed and need to be checked
- */
- const checkSettingsVisibility = (property: string, defaultVisibility: Visibility = Visibility.Local, allowedVisibility: Visibility[] = ALL_VISIBILITIES) => {
- getVisibilityButton(property)
- .scrollIntoView()
- validateActiveVisibility(property, defaultVisibility)
- allowedVisibility.forEach((active) => {
- setActiveVisibility(property, active)
- cy.reload()
- getVisibilityButton(property).scrollIntoView()
- validateActiveVisibility(property, active)
- })
- // TODO: Fix this in vue library then enable this test again
- /* // Test that not allowed options are disabled
- ALL_VISIBILITIES.filter((v) => !allowedVisibility.includes(v)).forEach((disabled) => {
- getVisibilityButton(property)
- .click()
- cy.get('ul[role="dialog"')
- .contains('button', disabled)
- .should('exist')
- .and('have.attr', 'disabled', 'true')
- }) */
- }
- const genericProperties = ['Location', 'X (formerly Twitter)', 'Fediverse']
- const nonfederatedProperties = ['Organisation', 'Role', 'Headline', 'About']
- describe('Settings: Change personal information', { testIsolation: true }, () => {
- before(() => {
- // ensure we can set locale and language
- cy.runOccCommand('config:system:delete force_language')
- cy.runOccCommand('config:system:delete force_locale')
- })
- after(() => {
- cy.runOccCommand('config:system:set force_language --value en')
- cy.runOccCommand('config:system:set force_locale --value en_US')
- })
- beforeEach(() => {
- cy.createRandomUser().then(($user) => {
- user = $user
- cy.modifyUser(user, 'language', 'en')
- cy.modifyUser(user, 'locale', 'en_US')
- cy.login($user)
- cy.visit('/settings/user')
- })
- cy.intercept('PUT', /ocs\/v2.php\/cloud\/users\//).as('submitSetting')
- })
- it('Can dis- and enable the profile', () => {
- cy.visit(`/u/${user.userId}`)
- cy.contains('h2', user.userId).should('be.visible')
- cy.visit('/settings/user')
- cy.contains('Enable profile').click()
- handlePasswordConfirmation(user.password)
- cy.visit(`/u/${user.userId}`, { failOnStatusCode: false })
- cy.contains('h2', 'Profile not found').should('be.visible')
- cy.visit('/settings/user')
- cy.contains('Enable profile').click()
- handlePasswordConfirmation(user.password)
- cy.visit(`/u/${user.userId}`, { failOnStatusCode: false })
- cy.contains('h2', user.userId).should('be.visible')
- })
- it('Can change language', () => {
- cy.intercept('GET', /settings\/user/).as('reload')
- inputForLabel('Language').scrollIntoView()
- inputForLabel('Language').type('Ned')
- cy.contains('li[role="option"]', 'Nederlands')
- .click()
- cy.wait('@reload')
- // expect language changed
- inputForLabel('Taal').scrollIntoView()
- cy.contains('section', 'Help met vertalen')
- })
- it('Can change locale', () => {
- cy.intercept('GET', /settings\/user/).as('reload')
- cy.clock(new Date(2024, 0, 10))
- // Default is US
- cy.contains('section', '01/10/2024')
- inputForLabel('Locale').scrollIntoView()
- inputForLabel('Locale').type('German')
- cy.contains('li[role="option"]', 'German (Germany')
- .click()
- cy.wait('@reload')
- // expect locale changed
- inputForLabel('Locale').scrollIntoView()
- cy.contains('section', '10.01.2024')
- })
- it('Can set primary email and change its visibility', () => {
- cy.contains('label', 'Email').scrollIntoView()
- // Check invalid input
- inputForLabel('Email').type('foo bar')
- inputForLabel('Email').then(($el) => expect(($el.get(0) as HTMLInputElement).checkValidity()).to.be.false)
- // handle valid input
- inputForLabel('Email').type('{selectAll}hello@example.com')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- cy.reload()
- inputForLabel('Email').should('have.value', 'hello@example.com')
- checkSettingsVisibility(
- 'Email',
- Visibility.Federated,
- // It is not possible to set it as private
- ALL_VISIBILITIES.filter((v) => v !== Visibility.Private),
- )
- // check it is visible on the profile
- cy.visit(`/u/${user.userId}`)
- cy.contains('a', 'hello@example.com').should('be.visible').and('have.attr', 'href', 'mailto:hello@example.com')
- })
- it('Can delete primary email', () => {
- cy.contains('label', 'Email').scrollIntoView()
- inputForLabel('Email').type('{selectAll}hello@example.com')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- // check after reload
- cy.reload()
- inputForLabel('Email').should('have.value', 'hello@example.com')
- // delete email
- cy.get('button[aria-label="Remove primary email"]').click({ force: true })
- cy.wait('@submitSetting')
- // check after reload
- cy.reload()
- inputForLabel('Email').should('have.value', '')
- })
- it('Can set and delete additional emails', () => {
- cy.get('button[aria-label="Add additional email"]').should('be.disabled')
- // we need a primary email first
- cy.contains('label', 'Email').scrollIntoView()
- inputForLabel('Email').type('{selectAll}primary@example.com')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- // add new email
- cy.get('button[aria-label="Add additional email"]')
- .click()
- // without any value we should not be able to add a second additional
- cy.get('button[aria-label="Add additional email"]').should('be.disabled')
- // fill the first additional
- inputForLabel('Additional email address 1')
- .type('1@example.com')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- // add second additional email
- cy.get('button[aria-label="Add additional email"]')
- .click()
- // fill the second additional
- inputForLabel('Additional email address 2')
- .type('2@example.com')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- // check the content is saved
- cy.reload()
- inputForLabel('Additional email address 1')
- .should('have.value', '1@example.com')
- inputForLabel('Additional email address 2')
- .should('have.value', '2@example.com')
- // delete the first
- cy.get('button[aria-label="Options for additional email address 1"]')
- .click({ force: true })
- cy.contains('button[role="menuitem"]', 'Delete email')
- .click({ force: true })
- handlePasswordConfirmation(user.password)
- cy.reload()
- inputForLabel('Additional email address 1')
- .should('have.value', '2@example.com')
- })
- it('Can set Full name and change its visibility', () => {
- cy.contains('label', 'Full name').scrollIntoView()
- // handle valid input
- inputForLabel('Full name').type('{selectAll}Jane Doe')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- cy.reload()
- inputForLabel('Full name').should('have.value', 'Jane Doe')
- checkSettingsVisibility(
- 'Full name',
- Visibility.Federated,
- // It is not possible to set it as private
- ALL_VISIBILITIES.filter((v) => v !== Visibility.Private),
- )
- // check it is visible on the profile
- cy.visit(`/u/${user.userId}`)
- cy.contains('h2', 'Jane Doe').should('be.visible')
- })
- it('Can set Phone number and its visibility', () => {
- cy.contains('label', 'Phone number').scrollIntoView()
- // Check invalid input
- inputForLabel('Phone number').type('foo bar')
- inputForLabel('Phone number').should('have.attr', 'class').and('contain', '--error')
- // handle valid input
- inputForLabel('Phone number').type('{selectAll}+49 89 721010 99701')
- inputForLabel('Phone number').should('have.attr', 'class').and('not.contain', '--error')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- cy.reload()
- inputForLabel('Phone number').should('have.value', '+498972101099701')
- checkSettingsVisibility('Phone number')
- // check it is visible on the profile
- cy.visit(`/u/${user.userId}`)
- cy.get('a[href="tel:+498972101099701"]').should('be.visible')
- })
- it('Can set Website and change its visibility', () => {
- cy.contains('label', 'Website').scrollIntoView()
- // Check invalid input
- inputForLabel('Website').type('foo bar')
- inputForLabel('Website').then(($el) => expect(($el.get(0) as HTMLInputElement).checkValidity()).to.be.false)
- // handle valid input
- inputForLabel('Website').type('{selectAll}http://example.com')
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- cy.reload()
- inputForLabel('Website').should('have.value', 'http://example.com')
- checkSettingsVisibility('Website')
- // check it is visible on the profile
- cy.visit(`/u/${user.userId}`)
- cy.contains('http://example.com').should('be.visible')
- })
- // Check generic properties that allow any visibility and any value
- genericProperties.forEach((property) => {
- it(`Can set ${property} and change its visibility`, () => {
- const uniqueValue = `${property.toUpperCase()} ${property.toLowerCase()}`
- cy.contains('label', property).scrollIntoView()
- inputForLabel(property).type(uniqueValue)
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- cy.reload()
- inputForLabel(property).should('have.value', uniqueValue)
- checkSettingsVisibility(property)
- // check it is visible on the profile
- cy.visit(`/u/${user.userId}`)
- cy.contains(uniqueValue).should('be.visible')
- })
- })
- // Check non federated properties - those where we need special configuration and only support local visibility
- nonfederatedProperties.forEach((property) => {
- it(`Can set ${property} and change its visibility`, () => {
- const uniqueValue = `${property.toUpperCase()} ${property.toLowerCase()}`
- cy.contains('label', property).scrollIntoView()
- inputForLabel(property).type(uniqueValue)
- handlePasswordConfirmation(user.password)
- cy.wait('@submitSetting')
- cy.reload()
- inputForLabel(property).should('have.value', uniqueValue)
- checkSettingsVisibility(property, Visibility.Local, [Visibility.Private, Visibility.Local])
- // check it is visible on the profile
- cy.visit(`/u/${user.userId}`)
- cy.contains(uniqueValue).should('be.visible')
- })
- })
- })
|