aboutsummaryrefslogtreecommitdiffstats
path: root/cypress/e2e/theming
diff options
context:
space:
mode:
Diffstat (limited to 'cypress/e2e/theming')
-rw-r--r--cypress/e2e/theming/a11y-color-contrast.cy.ts10
-rw-r--r--cypress/e2e/theming/admin-settings.cy.ts360
-rw-r--r--cypress/e2e/theming/admin-settings_default-app.cy.ts91
-rw-r--r--cypress/e2e/theming/admin-settings_urls.cy.ts143
-rw-r--r--cypress/e2e/theming/themingUtils.ts93
-rw-r--r--cypress/e2e/theming/user-settings_app-order.cy.ts (renamed from cypress/e2e/theming/navigation-bar-settings.cy.ts)228
-rw-r--r--cypress/e2e/theming/user-settings_background.cy.ts (renamed from cypress/e2e/theming/user-background.cy.ts)117
7 files changed, 668 insertions, 374 deletions
diff --git a/cypress/e2e/theming/a11y-color-contrast.cy.ts b/cypress/e2e/theming/a11y-color-contrast.cy.ts
index 03a6814ea1f..bff7df28e8e 100644
--- a/cypress/e2e/theming/a11y-color-contrast.cy.ts
+++ b/cypress/e2e/theming/a11y-color-contrast.cy.ts
@@ -1,3 +1,7 @@
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
const themesToTest = ['light', 'dark', 'light-highcontrast', 'dark-highcontrast']
const testCases = {
@@ -106,7 +110,7 @@ describe('Accessibility of Nextcloud theming colors', () => {
before(() => {
cy.createRandomUser().then(($user) => {
// set user theme
- cy.runOccCommand(`user:setting -- '${$user.userId}' theming enabled-themes '["${theme}"]'`)
+ cy.runOccCommand(`user:setting -- '${$user.userId}' theming enabled-themes '[\\"${theme}\\"]'`)
cy.login($user)
cy.visit('/')
cy.injectAxe({ axeCorePath: 'node_modules/axe-core/axe.min.js' })
@@ -118,7 +122,7 @@ describe('Accessibility of Nextcloud theming colors', () => {
// Unset background image and thus use background-color for testing blur background (images do not work with axe-core)
doc.body.style.backgroundImage = 'unset'
- const root = doc.querySelector('main')
+ const root = doc.querySelector('#content')
if (root === null) {
throw new Error('No test root found')
}
@@ -133,7 +137,7 @@ describe('Accessibility of Nextcloud theming colors', () => {
it(`color contrast of ${foreground} on ${background}`, () => {
cy.document().then(doc => {
const element = createTestCase(foreground, background)
- const root = doc.querySelector('main')
+ const root = doc.querySelector('#content')
// eslint-disable-next-line no-unused-expressions
expect(root).not.to.be.undefined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
diff --git a/cypress/e2e/theming/admin-settings.cy.ts b/cypress/e2e/theming/admin-settings.cy.ts
index 1c4e3458aae..4207b98f711 100644
--- a/cypress/e2e/theming/admin-settings.cy.ts
+++ b/cypress/e2e/theming/admin-settings.cy.ts
@@ -1,29 +1,19 @@
/**
- * @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
/* eslint-disable n/no-unpublished-import */
import { User } from '@nextcloud/cypress'
-import { colord } from 'colord'
-import { defaultPrimary, defaultBackground, pickRandomColor, validateBodyThemingCss, validateUserThemingDefaultCss } from './themingUtils'
+import {
+ defaultPrimary,
+ defaultBackground,
+ pickRandomColor,
+ validateBodyThemingCss,
+ validateUserThemingDefaultCss,
+ expectBackgroundColor,
+} from './themingUtils'
+import { NavigationHeader } from '../../pages/NavigationHeader'
const admin = new User('admin', 'admin')
@@ -36,15 +26,24 @@ describe('Admin theming settings visibility check', function() {
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
it('See the default settings', function() {
- cy.get('[data-admin-theming-setting-primary-color-picker]').should('exist')
- cy.get('[data-admin-theming-setting-primary-color-reset]').should('not.exist')
+ cy.get('[data-admin-theming-setting-color-picker]').should('exist')
cy.get('[data-admin-theming-setting-file-reset]').should('not.exist')
- cy.get('[data-admin-theming-setting-file-remove]').should('be.visible')
+ cy.get('[data-admin-theming-setting-file-remove]').should('exist')
+
+ cy.get(
+ '[data-admin-theming-setting-primary-color] [data-admin-theming-setting-color]',
+ ).then(($el) => expectBackgroundColor($el, defaultPrimary))
+
+ cy.get(
+ '[data-admin-theming-setting-background-color] [data-admin-theming-setting-color]',
+ ).then(($el) => expectBackgroundColor($el, defaultPrimary))
})
})
@@ -59,24 +58,42 @@ describe('Change the primary color and reset it', function() {
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
it('Change the primary color', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
- pickRandomColor().then(color => { selectedColor = color })
+ pickRandomColor('[data-admin-theming-setting-primary-color]').then(
+ (color) => {
+ selectedColor = color
+ },
+ )
cy.wait('@setColor')
- cy.waitUntil(() => validateBodyThemingCss(selectedColor, defaultBackground))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(
+ selectedColor,
+ defaultBackground,
+ defaultPrimary,
+ ),
+ )
})
it('Screenshot the login page and validate login page', function() {
cy.logout()
cy.visit('/')
- cy.waitUntil(() => validateBodyThemingCss(selectedColor, defaultBackground))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(
+ selectedColor,
+ defaultBackground,
+ defaultPrimary,
+ ),
+ )
cy.screenshot()
})
@@ -98,21 +115,29 @@ describe('Remove the default background and restore it', function() {
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
it('Remove the default background', function() {
- cy.intercept('*/apps/theming/ajax/updateStylesheet').as('removeBackground')
+ cy.intercept('*/apps/theming/ajax/updateStylesheet').as(
+ 'removeBackground',
+ )
cy.get('[data-admin-theming-setting-file-remove]').click()
cy.wait('@removeBackground')
cy.waitUntil(() => validateBodyThemingCss(defaultPrimary, null))
- cy.waitUntil(() => cy.window().then((win) => {
- const backgroundPlain = getComputedStyle(win.document.body).getPropertyValue('--image-background-plain')
- return backgroundPlain !== ''
- }))
+ cy.waitUntil(() =>
+ cy.window().then((win) => {
+ const backgroundPlain = getComputedStyle(
+ win.document.body,
+ ).getPropertyValue('--image-background')
+ return backgroundPlain !== ''
+ }),
+ )
})
it('Screenshot the login page and validate login page', function() {
@@ -132,7 +157,7 @@ describe('Remove the default background and restore it', function() {
})
})
-describe('Remove the default background with a custom primary color', function() {
+describe('Remove the default background with a custom background color', function() {
let selectedColor = ''
before(function() {
@@ -143,23 +168,40 @@ describe('Remove the default background with a custom primary color', function()
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
- it('Change the primary color', function() {
+ it('Change the background color', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
- pickRandomColor().then(color => { selectedColor = color })
+ pickRandomColor('[data-admin-theming-setting-background-color]').then(
+ (color) => {
+ selectedColor = color
+ },
+ )
cy.wait('@setColor')
- cy.waitUntil(() => validateBodyThemingCss(selectedColor, defaultBackground))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(
+ defaultPrimary,
+ defaultBackground,
+ selectedColor,
+ ),
+ )
})
it('Remove the default background', function() {
- cy.intercept('*/apps/theming/ajax/updateStylesheet').as('removeBackground')
+ cy.intercept('*/apps/theming/ajax/updateStylesheet').as(
+ 'removeBackground',
+ )
- cy.get('[data-admin-theming-setting-file-remove]').click()
+ cy.get('[data-admin-theming-setting-file-remove]').scrollIntoView()
+ cy.get('[data-admin-theming-setting-file-remove]').click({
+ force: true,
+ })
cy.wait('@removeBackground')
})
@@ -168,7 +210,9 @@ describe('Remove the default background with a custom primary color', function()
cy.logout()
cy.visit('/')
- cy.waitUntil(() => validateBodyThemingCss(selectedColor, null))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(defaultPrimary, null, selectedColor),
+ )
cy.screenshot()
})
@@ -182,6 +226,9 @@ describe('Remove the default background with a custom primary color', function()
})
describe('Remove the default background with a bright color', function() {
+ const navigationHeader = new NavigationHeader()
+ let selectedColor = ''
+
before(function() {
// Just in case previous test failed
cy.resetAdminTheming()
@@ -191,37 +238,52 @@ describe('Remove the default background with a bright color', function() {
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
it('Remove the default background', function() {
- cy.intercept('*/apps/theming/ajax/updateStylesheet').as('removeBackground')
+ cy.intercept('*/apps/theming/ajax/updateStylesheet').as(
+ 'removeBackground',
+ )
cy.get('[data-admin-theming-setting-file-remove]').click()
cy.wait('@removeBackground')
})
- it('Change the primary color', function() {
+ it('Change the background color', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
// Pick one of the bright color preset
- cy.get('[data-admin-theming-setting-primary-color-picker]').click()
- cy.get('.color-picker__simple-color-circle:eq(4)').click()
+ pickRandomColor(
+ '[data-admin-theming-setting-background-color]',
+ 4,
+ ).then((color) => {
+ selectedColor = color
+ })
cy.wait('@setColor')
- cy.waitUntil(() => validateBodyThemingCss('#ddcb55', null))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(defaultPrimary, null, selectedColor),
+ )
})
it('See the header being inverted', function() {
- cy.waitUntil(() => cy.window().then((win) => {
- const firstEntry = win.document.querySelector('.app-menu-main li img')
- if (!firstEntry) {
- return false
- }
- return getComputedStyle(firstEntry).filter === 'invert(1)'
- }))
+ cy.waitUntil(() =>
+ navigationHeader
+ .getNavigationEntries()
+ .find('img')
+ .then((el) => {
+ let ret = true
+ el.each(function() {
+ ret = ret && window.getComputedStyle(this).filter === 'invert(1)'
+ })
+ return ret
+ })
+ )
})
})
@@ -238,7 +300,9 @@ describe('Change the login fields then reset them', function() {
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
@@ -246,42 +310,54 @@ describe('Change the login fields then reset them', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('updateFields')
// Name
- cy.get('[data-admin-theming-setting-field="name"] input[type="text"]')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-field="name"] input[type="text"]')
- .type(`{selectall}${name}{enter}`)
+ cy.get(
+ '[data-admin-theming-setting-field="name"] input[type="text"]',
+ ).scrollIntoView()
+ cy.get(
+ '[data-admin-theming-setting-field="name"] input[type="text"]',
+ ).type(`{selectall}${name}{enter}`)
cy.wait('@updateFields')
// Url
- cy.get('[data-admin-theming-setting-field="url"] input[type="url"]')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-field="url"] input[type="url"]')
- .type(`{selectall}${url}{enter}`)
+ cy.get(
+ '[data-admin-theming-setting-field="url"] input[type="url"]',
+ ).scrollIntoView()
+ cy.get(
+ '[data-admin-theming-setting-field="url"] input[type="url"]',
+ ).type(`{selectall}${url}{enter}`)
cy.wait('@updateFields')
// Slogan
- cy.get('[data-admin-theming-setting-field="slogan"] input[type="text"]')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-field="slogan"] input[type="text"]')
- .type(`{selectall}${slogan}{enter}`)
+ cy.get(
+ '[data-admin-theming-setting-field="slogan"] input[type="text"]',
+ ).scrollIntoView()
+ cy.get(
+ '[data-admin-theming-setting-field="slogan"] input[type="text"]',
+ ).type(`{selectall}${slogan}{enter}`)
cy.wait('@updateFields')
})
it('Ensure undo button presence', function() {
- cy.get('[data-admin-theming-setting-field="name"] .input-field__trailing-button')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-field="name"] .input-field__trailing-button')
- .should('be.visible')
-
- cy.get('[data-admin-theming-setting-field="url"] .input-field__trailing-button')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-field="url"] .input-field__trailing-button')
- .should('be.visible')
-
- cy.get('[data-admin-theming-setting-field="slogan"] .input-field__trailing-button')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-field="slogan"] .input-field__trailing-button')
- .should('be.visible')
+ cy.get(
+ '[data-admin-theming-setting-field="name"] .input-field__trailing-button',
+ ).scrollIntoView()
+ cy.get(
+ '[data-admin-theming-setting-field="name"] .input-field__trailing-button',
+ ).should('be.visible')
+
+ cy.get(
+ '[data-admin-theming-setting-field="url"] .input-field__trailing-button',
+ ).scrollIntoView()
+ cy.get(
+ '[data-admin-theming-setting-field="url"] .input-field__trailing-button',
+ ).should('be.visible')
+
+ cy.get(
+ '[data-admin-theming-setting-field="slogan"] .input-field__trailing-button',
+ ).scrollIntoView()
+ cy.get(
+ '[data-admin-theming-setting-field="slogan"] .input-field__trailing-button',
+ ).should('be.visible')
})
it('Validate login screen changes', function() {
@@ -317,19 +393,29 @@ describe('Disable user theming and enable it back', function() {
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
it('Disable user background theming', function() {
- cy.intercept('*/apps/theming/ajax/updateStylesheet').as('disableUserTheming')
-
- cy.get('[data-admin-theming-setting-disable-user-theming]')
- .scrollIntoView()
- cy.get('[data-admin-theming-setting-disable-user-theming]')
- .should('be.visible')
- cy.get('[data-admin-theming-setting-disable-user-theming] input[type="checkbox"]').check({ force: true })
- cy.get('[data-admin-theming-setting-disable-user-theming] input[type="checkbox"]').should('be.checked')
+ cy.intercept('*/apps/theming/ajax/updateStylesheet').as(
+ 'disableUserTheming',
+ )
+
+ cy.get(
+ '[data-admin-theming-setting-disable-user-theming]',
+ ).scrollIntoView()
+ cy.get('[data-admin-theming-setting-disable-user-theming]').should(
+ 'be.visible',
+ )
+ cy.get(
+ '[data-admin-theming-setting-disable-user-theming] input[type="checkbox"]',
+ ).check({ force: true })
+ cy.get(
+ '[data-admin-theming-setting-disable-user-theming] input[type="checkbox"]',
+ ).should('be.checked')
cy.wait('@disableUserTheming')
})
@@ -343,8 +429,9 @@ describe('Disable user theming and enable it back', function() {
it('User cannot not change background settings', function() {
cy.visit('/settings/user/theming')
- cy.get('[data-user-theming-background-disabled]').scrollIntoView()
- cy.get('[data-user-theming-background-disabled]').should('be.visible')
+ cy.contains(
+ 'Customization has been disabled by your administrator',
+ ).should('exist')
})
})
@@ -363,40 +450,60 @@ describe('The user default background settings reflect the admin theming setting
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
- it('Change the primary color', function() {
- cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
-
- pickRandomColor().then(color => { selectedColor = color })
-
- cy.wait('@setColor')
- cy.waitUntil(() => cy.window().then(($window) => {
- const primary = $window.getComputedStyle($window.document.body).getPropertyValue('--color-primary-default')
- return colord(primary).isEqual(selectedColor)
- }))
- })
-
it('Change the default background', function() {
cy.intercept('*/apps/theming/ajax/uploadImage').as('setBackground')
cy.fixture('image.jpg', null).as('background')
- cy.get('[data-admin-theming-setting-file="background"] input[type="file"]').selectFile('@background', { force: true })
+ cy.get(
+ '[data-admin-theming-setting-file="background"] input[type="file"]',
+ ).selectFile('@background', { force: true })
cy.wait('@setBackground')
- cy.waitUntil(() => cy.window().then((win) => {
- const currentBackgroundDefault = getComputedStyle(win.document.body).getPropertyValue('--image-background-default')
- return currentBackgroundDefault.includes('/apps/theming/image/background?v=')
- }))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(
+ defaultPrimary,
+ '/apps/theming/image/background?v=',
+ null,
+ ),
+ )
+ })
+
+ it('Change the background color', function() {
+ cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
+
+ pickRandomColor('[data-admin-theming-setting-background-color]').then(
+ (color) => {
+ selectedColor = color
+ },
+ )
+
+ cy.wait('@setColor')
+ cy.waitUntil(() =>
+ validateBodyThemingCss(
+ defaultPrimary,
+ '/apps/theming/image/background?v=',
+ selectedColor,
+ ),
+ )
})
it('Login page should match admin theming settings', function() {
cy.logout()
cy.visit('/')
- cy.waitUntil(() => validateBodyThemingCss(selectedColor, '/apps/theming/image/background?v='))
+ cy.waitUntil(() =>
+ validateBodyThemingCss(
+ defaultPrimary,
+ '/apps/theming/image/background?v=',
+ selectedColor,
+ ),
+ )
})
it('Login as user', function() {
@@ -413,9 +520,17 @@ describe('The user default background settings reflect the admin theming setting
it('Default user background settings should match admin theming settings', function() {
cy.get('[data-user-theming-background-default]').should('be.visible')
- cy.get('[data-user-theming-background-default]').should('have.class', 'background--active')
-
- cy.waitUntil(() => validateUserThemingDefaultCss(selectedColor, '/apps/theming/image/background?v='))
+ cy.get('[data-user-theming-background-default]').should(
+ 'have.class',
+ 'background--active',
+ )
+
+ cy.waitUntil(() =>
+ validateUserThemingDefaultCss(
+ selectedColor,
+ '/apps/theming/image/background?v=',
+ ),
+ )
})
})
@@ -432,12 +547,16 @@ describe('The user default background settings reflect the admin theming setting
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
- cy.get('[data-admin-theming-settings]').should('exist').scrollIntoView()
+ cy.get('[data-admin-theming-settings]')
+ .should('exist')
+ .scrollIntoView()
cy.get('[data-admin-theming-settings]').should('be.visible')
})
it('Remove the default background', function() {
- cy.intercept('*/apps/theming/ajax/updateStylesheet').as('removeBackground')
+ cy.intercept('*/apps/theming/ajax/updateStylesheet').as(
+ 'removeBackground',
+ )
cy.get('[data-admin-theming-setting-file-remove]').click()
@@ -466,7 +585,10 @@ describe('The user default background settings reflect the admin theming setting
it('Default user background settings should match admin theming settings', function() {
cy.get('[data-user-theming-background-default]').should('be.visible')
- cy.get('[data-user-theming-background-default]').should('have.class', 'background--active')
+ cy.get('[data-user-theming-background-default]').should(
+ 'have.class',
+ 'background--active',
+ )
cy.waitUntil(() => validateUserThemingDefaultCss(defaultPrimary, null))
})
diff --git a/cypress/e2e/theming/admin-settings_default-app.cy.ts b/cypress/e2e/theming/admin-settings_default-app.cy.ts
new file mode 100644
index 00000000000..702f737bc15
--- /dev/null
+++ b/cypress/e2e/theming/admin-settings_default-app.cy.ts
@@ -0,0 +1,91 @@
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { User } from '@nextcloud/cypress'
+import { NavigationHeader } from '../../pages/NavigationHeader'
+
+const admin = new User('admin', 'admin')
+
+describe('Admin theming set default apps', () => {
+ const navigationHeader = new NavigationHeader()
+
+ before(function() {
+ // Just in case previous test failed
+ cy.resetAdminTheming()
+ cy.login(admin)
+ })
+
+ it('See the current default app is the dashboard', () => {
+ // check default route
+ cy.visit('/')
+ cy.url().should('match', /apps\/dashboard/)
+
+ // Also check the top logo link
+ navigationHeader.logo().click()
+ cy.url().should('match', /apps\/dashboard/)
+ })
+
+ it('See the default app settings', () => {
+ cy.visit('/settings/admin/theming')
+
+ cy.get('.settings-section').contains('Navigation bar settings').should('exist')
+ cy.get('[data-cy-switch-default-app]').should('exist')
+ cy.get('[data-cy-switch-default-app]').scrollIntoView()
+ })
+
+ it('Toggle the "use custom default app" switch', () => {
+ cy.get('[data-cy-switch-default-app] input').should('not.be.checked')
+ cy.get('[data-cy-switch-default-app] .checkbox-content').click()
+ cy.get('[data-cy-switch-default-app] input').should('be.checked')
+ })
+
+ it('See the default app order selector', () => {
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
+ const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
+ expect(appIDs).to.deep.eq(['dashboard', 'files'])
+ })
+ })
+
+ it('Change the default app', () => {
+ cy.get('[data-cy-app-order] [data-cy-app-order-element="files"]').scrollIntoView()
+
+ cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('be.visible')
+ cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').click()
+ cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('not.be.visible')
+
+ })
+
+ it('See the default app is changed', () => {
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
+ const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
+ expect(appIDs).to.deep.eq(['files', 'dashboard'])
+ })
+
+ // Check the redirect to the default app works
+ cy.request({ url: '/', followRedirect: false }).then((response) => {
+ expect(response.status).to.eq(302)
+ expect(response).to.have.property('headers')
+ expect(response.headers.location).to.contain('/apps/files')
+ })
+ })
+
+ it('Toggle the "use custom default app" switch back to reset the default apps', () => {
+ cy.visit('/settings/admin/theming')
+ cy.get('[data-cy-switch-default-app]').scrollIntoView()
+
+ cy.get('[data-cy-switch-default-app] input').should('be.checked')
+ cy.get('[data-cy-switch-default-app] .checkbox-content').click()
+ cy.get('[data-cy-switch-default-app] input').should('be.not.checked')
+ })
+
+ it('See the default app is changed back to default', () => {
+ // Check the redirect to the default app works
+ cy.request({ url: '/', followRedirect: false }).then((response) => {
+ expect(response.status).to.eq(302)
+ expect(response).to.have.property('headers')
+ expect(response.headers.location).to.contain('/apps/dashboard')
+ })
+ })
+})
diff --git a/cypress/e2e/theming/admin-settings_urls.cy.ts b/cypress/e2e/theming/admin-settings_urls.cy.ts
new file mode 100644
index 00000000000..46bae7901c4
--- /dev/null
+++ b/cypress/e2e/theming/admin-settings_urls.cy.ts
@@ -0,0 +1,143 @@
+/*!
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { User } from '@nextcloud/cypress'
+
+const admin = new User('admin', 'admin')
+
+describe('Admin theming: Setting custom project URLs', function() {
+ this.beforeEach(() => {
+ // Just in case previous test failed
+ cy.resetAdminTheming()
+ cy.login(admin)
+ cy.visit('/settings/admin/theming')
+ cy.intercept('POST', '**/apps/theming/ajax/updateStylesheet').as('updateTheming')
+ })
+
+ it('Setting the web link', () => {
+ cy.findByRole('textbox', { name: /web link/i })
+ .and('have.attr', 'type', 'url')
+ .as('input')
+ .scrollIntoView()
+ cy.get('@input')
+ .should('be.visible')
+ .type('{selectAll}http://example.com/path?query#fragment{enter}')
+
+ cy.wait('@updateTheming')
+
+ cy.logout()
+
+ cy.visit('/')
+ cy.contains('a', 'Nextcloud')
+ .should('be.visible')
+ .and('have.attr', 'href', 'http://example.com/path?query#fragment')
+ })
+
+ it('Setting the legal notice link', () => {
+ cy.findByRole('textbox', { name: /legal notice link/i })
+ .should('exist')
+ .and('have.attr', 'type', 'url')
+ .as('input')
+ .scrollIntoView()
+ cy.get('@input')
+ .type('http://example.com/path?query#fragment{enter}')
+
+ cy.wait('@updateTheming')
+
+ cy.logout()
+
+ cy.visit('/')
+ cy.contains('a', /legal notice/i)
+ .should('be.visible')
+ .and('have.attr', 'href', 'http://example.com/path?query#fragment')
+ })
+
+ it('Setting the privacy policy link', () => {
+ cy.findByRole('textbox', { name: /privacy policy link/i })
+ .should('exist')
+ .as('input')
+ .scrollIntoView()
+ cy.get('@input')
+ .should('have.attr', 'type', 'url')
+ .type('http://privacy.local/path?query#fragment{enter}')
+
+ cy.wait('@updateTheming')
+
+ cy.logout()
+
+ cy.visit('/')
+ cy.contains('a', /privacy policy/i)
+ .should('be.visible')
+ .and('have.attr', 'href', 'http://privacy.local/path?query#fragment')
+ })
+
+})
+
+describe('Admin theming: Web link corner cases', function() {
+ this.beforeEach(() => {
+ // Just in case previous test failed
+ cy.resetAdminTheming()
+ cy.login(admin)
+ cy.visit('/settings/admin/theming')
+ cy.intercept('POST', '**/apps/theming/ajax/updateStylesheet').as('updateTheming')
+ })
+
+ it('Already URL encoded', () => {
+ cy.findByRole('textbox', { name: /web link/i })
+ .and('have.attr', 'type', 'url')
+ .as('input')
+ .scrollIntoView()
+ cy.get('@input')
+ .should('be.visible')
+ .type('{selectAll}http://example.com/%22path%20with%20space%22{enter}')
+
+ cy.wait('@updateTheming')
+
+ cy.logout()
+
+ cy.visit('/')
+ cy.contains('a', 'Nextcloud')
+ .should('be.visible')
+ .and('have.attr', 'href', 'http://example.com/%22path%20with%20space%22')
+ })
+
+ it('URL with double quotes', () => {
+ cy.findByRole('textbox', { name: /web link/i })
+ .and('have.attr', 'type', 'url')
+ .as('input')
+ .scrollIntoView()
+ cy.get('@input')
+ .should('be.visible')
+ .type('{selectAll}http://example.com/"path"{enter}')
+
+ cy.wait('@updateTheming')
+
+ cy.logout()
+
+ cy.visit('/')
+ cy.contains('a', 'Nextcloud')
+ .should('be.visible')
+ .and('have.attr', 'href', 'http://example.com/%22path%22')
+ })
+
+ it('URL with double quotes and already encoded', () => {
+ cy.findByRole('textbox', { name: /web link/i })
+ .and('have.attr', 'type', 'url')
+ .as('input')
+ .scrollIntoView()
+ cy.get('@input')
+ .should('be.visible')
+ .type('{selectAll}http://example.com/"the%20path"{enter}')
+
+ cy.wait('@updateTheming')
+
+ cy.logout()
+
+ cy.visit('/')
+ cy.contains('a', 'Nextcloud')
+ .should('be.visible')
+ .and('have.attr', 'href', 'http://example.com/%22the%20path%22')
+ })
+
+})
diff --git a/cypress/e2e/theming/themingUtils.ts b/cypress/e2e/theming/themingUtils.ts
index 2965886c656..b4740beda1c 100644
--- a/cypress/e2e/theming/themingUtils.ts
+++ b/cypress/e2e/theming/themingUtils.ts
@@ -1,49 +1,57 @@
/**
- * @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { colord } from 'colord'
-const defaultNextcloudBlue = '#0082c9'
export const defaultPrimary = '#00679e'
-export const defaultBackground = 'kamil-porembinski-clouds.jpg'
+export const defaultBackground = 'jenna-kim-the-globe.webp'
+
+/**
+ * Check if a CSS variable is set to a specific color
+ * @param variable Variable to check
+ * @param expectedColor Color that is expected
+ */
+export function validateCSSVariable(variable: string, expectedColor: string) {
+ const value = window.getComputedStyle(Cypress.$('body').get(0)).getPropertyValue(variable)
+ console.debug(`${variable}, is: ${colord(value).toHex()} expected: ${expectedColor}`)
+ return colord(value).isEqual(expectedColor)
+}
/**
* Validate the current page body css variables
*
- * @param {string} expectedColor the expected color
+ * @param {string} expectedColor the expected primary color
* @param {string|null} expectedBackground the expected background
+ * @param {string|null} expectedBackgroundColor the expected background color (null to ignore)
*/
-export const validateBodyThemingCss = function(expectedColor = defaultPrimary, expectedBackground: string|null = defaultBackground) {
+export function validateBodyThemingCss(expectedColor = defaultPrimary, expectedBackground: string|null = defaultBackground, expectedBackgroundColor: string|null = defaultPrimary) {
// We must use `Cypress.$` here as any assertions (get is an assertion) is not allowed in wait-until's check function, see documentation
const guestBackgroundColor = Cypress.$('body').css('background-color')
const guestBackgroundImage = Cypress.$('body').css('background-image')
- const isValidBackgroundColor = colord(guestBackgroundColor).isEqual(expectedColor)
+ const isValidBackgroundColor = expectedBackgroundColor === null || colord(guestBackgroundColor).isEqual(expectedBackgroundColor)
const isValidBackgroundImage = !expectedBackground
? guestBackgroundImage === 'none'
: guestBackgroundImage.includes(expectedBackground)
- console.debug({ guestBackgroundColor: colord(guestBackgroundColor).toHex(), guestBackgroundImage, expectedColor, expectedBackground, isValidBackgroundColor, isValidBackgroundImage })
+ console.debug({
+ isValidBackgroundColor,
+ isValidBackgroundImage,
+ guestBackgroundColor: colord(guestBackgroundColor).toHex(),
+ guestBackgroundImage,
+ })
- return isValidBackgroundColor && isValidBackgroundImage
+ return isValidBackgroundColor && isValidBackgroundImage && validateCSSVariable('--color-primary', expectedColor)
+}
+
+/**
+ * Check background color of element
+ * @param element JQuery element to check
+ * @param color expected color
+ */
+export function expectBackgroundColor(element: JQuery<HTMLElement>, color: string) {
+ expect(colord(element.css('background-color')).toHex()).equal(colord(color).toHex())
}
/**
@@ -58,28 +66,28 @@ export const validateUserThemingDefaultCss = function(expectedColor = defaultPri
return false
}
- const defaultOptionBackground = defaultSelectButton.css('background-image')
- const colorPickerOptionColor = defaultSelectButton.css('background-color')
- const isNextcloudBlue = colord(colorPickerOptionColor).isEqual('#0082c9')
+ const backgroundImage = defaultSelectButton.css('background-image')
+ const backgroundColor = defaultSelectButton.css('background-color')
const isValidBackgroundImage = !expectedBackground
- ? defaultOptionBackground === 'none'
- : defaultOptionBackground.includes(expectedBackground)
-
- console.debug({ colorPickerOptionColor: colord(colorPickerOptionColor).toHex(), expectedColor, isValidBackgroundImage, isNextcloudBlue })
+ ? (backgroundImage === 'none' || Cypress.$('body').css('background-image') === 'none')
+ : backgroundImage.includes(expectedBackground)
+
+ console.debug({
+ colorPickerOptionColor: colord(backgroundColor).toHex(),
+ expectedColor,
+ isValidBackgroundImage,
+ backgroundImage,
+ })
- return isValidBackgroundImage && (
- colord(colorPickerOptionColor).isEqual(expectedColor)
- // we replace nextcloud blue with the the default rpimary (apps/theming/lib/Themes/DefaultTheme.php line 76)
- || (isNextcloudBlue && colord(expectedColor).isEqual(defaultPrimary))
- )
+ return isValidBackgroundImage && colord(backgroundColor).isEqual(expectedColor)
}
-export const pickRandomColor = function(): Cypress.Chainable<string> {
+export const pickRandomColor = function(context: string, index?: number): Cypress.Chainable<string> {
// Pick one of the first 8 options
- const randColour = Math.floor(Math.random() * 8)
+ const randColour = index ?? Math.floor(Math.random() * 8)
- const colorPreviewSelector = '[data-user-theming-background-color],[data-admin-theming-setting-primary-color]'
+ const colorPreviewSelector = `${context} [data-admin-theming-setting-color]`
let oldColor = ''
cy.get(colorPreviewSelector).then(($el) => {
@@ -87,7 +95,8 @@ export const pickRandomColor = function(): Cypress.Chainable<string> {
})
// Open picker
- cy.contains('button', 'Change color').click()
+ cy.get(`${context} [data-admin-theming-setting-color-picker]`).scrollIntoView()
+ cy.get(`${context} [data-admin-theming-setting-color-picker]`).click({ force: true })
// Click on random color
cy.get('.color-picker__simple-color-circle').eq(randColour).click()
diff --git a/cypress/e2e/theming/navigation-bar-settings.cy.ts b/cypress/e2e/theming/user-settings_app-order.cy.ts
index 4bea6225f76..11ef2f45382 100644
--- a/cypress/e2e/theming/navigation-bar-settings.cy.ts
+++ b/cypress/e2e/theming/user-settings_app-order.cy.ts
@@ -1,110 +1,23 @@
/**
- * @copyright Copyright (c) 2023 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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
import { installTestApp, uninstallTestApp } from '../../support/commonUtils'
+import { NavigationHeader } from '../../pages/NavigationHeader'
-const admin = new User('admin', 'admin')
-
-describe('Admin theming set default apps', () => {
- before(function() {
- // Just in case previous test failed
- cy.resetAdminTheming()
- cy.login(admin)
- })
-
- it('See the current default app is the dashboard', () => {
- cy.visit('/')
- cy.url().should('match', /apps\/dashboard/)
-
- // Also check the top logo link
- cy.get('#nextcloud').click()
- cy.url().should('match', /apps\/dashboard/)
- })
-
- it('See the default app settings', () => {
- cy.visit('/settings/admin/theming')
-
- cy.get('.settings-section').contains('Navigation bar settings').should('exist')
- cy.get('[data-cy-switch-default-app]').should('exist')
- cy.get('[data-cy-switch-default-app]').scrollIntoView()
- })
-
- it('Toggle the "use custom default app" switch', () => {
- cy.get('[data-cy-switch-default-app] input').should('not.be.checked')
- cy.get('[data-cy-switch-default-app] .checkbox-content').click()
- cy.get('[data-cy-switch-default-app] input').should('be.checked')
- })
-
- it('See the default app order selector', () => {
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['dashboard', 'files'])
- })
- })
-
- it('Change the default app', () => {
- cy.get('[data-cy-app-order] [data-cy-app-order-element="files"]').scrollIntoView()
-
- cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('be.visible')
- cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').click()
- cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('not.be.visible')
-
- })
-
- it('See the default app is changed', () => {
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['files', 'dashboard'])
- })
-
- // Check the redirect to the default app works
- cy.request({ url: '/', followRedirect: false }).then((response) => {
- expect(response.status).to.eq(302)
- expect(response).to.have.property('headers')
- expect(response.headers.location).to.contain('/apps/files')
- })
- })
-
- it('Toggle the "use custom default app" switch back to reset the default apps', () => {
- cy.visit('/settings/admin/theming')
- cy.get('[data-cy-switch-default-app]').scrollIntoView()
-
- cy.get('[data-cy-switch-default-app] input').should('be.checked')
- cy.get('[data-cy-switch-default-app] .checkbox-content').click()
- cy.get('[data-cy-switch-default-app] input').should('be.not.checked')
- })
+/**
+ * Intercept setting the app order as `updateAppOrder`
+ */
+function interceptAppOrder() {
+ cy.intercept('POST', '/ocs/v2.php/apps/provisioning_api/api/v1/config/users/core/apporder').as('updateAppOrder')
+}
- it('See the default app is changed back to default', () => {
- // Check the redirect to the default app works
- cy.request({ url: '/', followRedirect: false }).then((response) => {
- expect(response.status).to.eq(302)
- expect(response).to.have.property('headers')
- expect(response.headers.location).to.contain('/apps/dashboard')
- })
- })
-})
+before(() => uninstallTestApp())
describe('User theming set app order', () => {
+ const navigationHeader = new NavigationHeader()
let user: User
before(() => {
@@ -126,40 +39,43 @@ describe('User theming set app order', () => {
})
it('See that the dashboard app is the first one', () => {
+ const appOrder = ['Dashboard', 'Files']
// Check the app order settings UI
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['dashboard', 'files'])
- })
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
// Check the top app menu order
- cy.get('.app-menu-main .app-menu-entry').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-app-id')).get()
- expect(appIDs).to.deep.eq(['dashboard', 'files'])
- })
+ navigationHeader.getNavigationEntries()
+ .each((entry, index) => expect(entry).contain.text(appOrder[index]))
})
it('Change the app order', () => {
+ interceptAppOrder()
cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('be.visible')
cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').click()
cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('not.be.visible')
+ cy.wait('@updateAppOrder')
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['files', 'dashboard'])
- })
+ const appOrder = ['Files', 'Dashboard']
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
})
it('See the app menu order is changed', () => {
cy.reload()
- cy.get('.app-menu-main .app-menu-entry').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-app-id')).get()
- expect(appIDs).to.deep.eq(['files', 'dashboard'])
- })
+ const appOrder = ['Files', 'Dashboard']
+ // Check the app order settings UI
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
+
+ // Check the top app menu order
+ navigationHeader.getNavigationEntries()
+ .each((entry, index) => expect(entry).contain.text(appOrder[index]))
})
})
describe('User theming set app order with default app', () => {
+ const navigationHeader = new NavigationHeader()
let user: User
before(() => {
@@ -167,7 +83,7 @@ describe('User theming set app order with default app', () => {
// install a third app
installTestApp()
// set files as default app
- cy.runOccCommand('config:system:set --value "files" defaultapp')
+ cy.runOccCommand('config:system:set --value \'files\' defaultapp')
// Create random user for this test
cy.createRandomUser().then(($user) => {
@@ -193,11 +109,11 @@ describe('User theming set app order with default app', () => {
it('See the app order settings: files is the first one', () => {
cy.visit('/settings/user/theming')
cy.get('[data-cy-app-order]').scrollIntoView()
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- expect(elements).to.have.length(4)
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['files', 'dashboard', 'testapp1', 'testapp'])
- })
+
+ const appOrder = ['Files', 'Dashboard', 'Test App 2', 'Test App']
+ // Check the app order settings UI
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
})
it('Can not change the default app', () => {
@@ -212,32 +128,31 @@ describe('User theming set app order with default app', () => {
})
it('Change the order of the other apps', () => {
- cy.intercept('POST', '**/apps/provisioning_api/api/v1/config/users/core/apporder').as('setAppOrder')
+ interceptAppOrder()
// Move the testapp up twice, it should be the first one after files
cy.get('[data-cy-app-order] [data-cy-app-order-element="testapp"] [data-cy-app-order-button="up"]').click()
- cy.wait('@setAppOrder')
+ cy.wait('@updateAppOrder')
cy.get('[data-cy-app-order] [data-cy-app-order-element="testapp"] [data-cy-app-order-button="up"]').click()
- cy.wait('@setAppOrder')
+ cy.wait('@updateAppOrder')
// Can't get up anymore, files is enforced as default app
cy.get('[data-cy-app-order] [data-cy-app-order-element="testapp"] [data-cy-app-order-button="up"]').should('not.be.visible')
// Check the final list order
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- expect(elements).to.have.length(4)
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['files', 'testapp', 'dashboard', 'testapp1'])
- })
+ const appOrder = ['Files', 'Test App', 'Dashboard', 'Test App 2']
+ // Check the app order settings UI
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
})
it('See the app menu order is changed', () => {
cy.reload()
- cy.get('.app-menu-main .app-menu-entry').then(elements => {
- expect(elements).to.have.length(4)
- const appIDs = elements.map((idx, el) => el.getAttribute('data-app-id')).get()
- expect(appIDs).to.deep.eq(['files', 'testapp', 'dashboard', 'testapp1'])
- })
+
+ const appOrder = ['Files', 'Test App', 'Dashboard', 'Test App 2']
+ // Check the top app menu order
+ navigationHeader.getNavigationEntries()
+ .each((entry, index) => expect(entry).contain.text(appOrder[index]))
})
})
@@ -264,8 +179,10 @@ describe('User theming app order list accessibility', () => {
})
it('click the first button', () => {
+ interceptAppOrder()
cy.get('[data-cy-app-order] [data-cy-app-order-element="dashboard"] [data-cy-app-order-button="down"]').should('be.visible').focus()
cy.get('[data-cy-app-order] [data-cy-app-order-element="dashboard"] [data-cy-app-order-button="down"]').click()
+ cy.wait('@updateAppOrder')
})
it('see the same app kept the focus', () => {
@@ -276,8 +193,10 @@ describe('User theming app order list accessibility', () => {
})
it('click the last button', () => {
+ interceptAppOrder()
cy.get('[data-cy-app-order] [data-cy-app-order-element="dashboard"] [data-cy-app-order-button="up"]').should('be.visible').focus()
cy.get('[data-cy-app-order] [data-cy-app-order-element="dashboard"] [data-cy-app-order-button="up"]').click()
+ cy.wait('@updateAppOrder')
})
it('see the same app kept the focus', () => {
@@ -289,6 +208,7 @@ describe('User theming app order list accessibility', () => {
})
describe('User theming reset app order', () => {
+ const navigationHeader = new NavigationHeader()
let user: User
before(() => {
@@ -310,17 +230,14 @@ describe('User theming reset app order', () => {
})
it('See that the dashboard app is the first one', () => {
+ const appOrder = ['Dashboard', 'Files']
// Check the app order settings UI
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['dashboard', 'files'])
- })
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
// Check the top app menu order
- cy.get('.app-menu-main .app-menu-entry').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-app-id')).get()
- expect(appIDs).to.deep.eq(['dashboard', 'files'])
- })
+ navigationHeader.getNavigationEntries()
+ .each((entry, index) => expect(entry).contain.text(appOrder[index]))
})
it('See the reset button is disabled', () => {
@@ -329,15 +246,17 @@ describe('User theming reset app order', () => {
})
it('Change the app order', () => {
+ interceptAppOrder()
cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('be.visible')
cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').click()
cy.get('[data-cy-app-order] [data-cy-app-order-element="files"] [data-cy-app-order-button="up"]').should('not.be.visible')
+ cy.wait('@updateAppOrder')
// Check the app order settings UI
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['files', 'dashboard'])
- })
+ const appOrder = ['Files', 'Dashboard']
+ // Check the app order settings UI
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
})
it('See the reset button is no longer disabled', () => {
@@ -346,14 +265,25 @@ describe('User theming reset app order', () => {
})
it('Reset the app order', () => {
+ cy.intercept('GET', '/ocs/v2.php/core/navigation/apps').as('loadApps')
+ interceptAppOrder()
cy.get('[data-test-id="btn-apporder-reset"]').click({ force: true })
+
+ cy.wait('@updateAppOrder')
+ .its('request.body')
+ .should('have.property', 'configValue', '[]')
+ cy.wait('@loadApps')
})
it('See the app order is restored', () => {
- cy.get('[data-cy-app-order] [data-cy-app-order-element]').then(elements => {
- const appIDs = elements.map((idx, el) => el.getAttribute('data-cy-app-order-element')).get()
- expect(appIDs).to.deep.eq(['dashboard', 'files'])
- })
+ const appOrder = ['Dashboard', 'Files']
+ // Check the app order settings UI
+ cy.get('[data-cy-app-order] [data-cy-app-order-element]')
+ .each((element, index) => expect(element).to.contain.text(appOrder[index]))
+
+ // Check the top app menu order
+ navigationHeader.getNavigationEntries()
+ .each((entry, index) => expect(entry).contain.text(appOrder[index]))
})
it('See the reset button is disabled again', () => {
diff --git a/cypress/e2e/theming/user-background.cy.ts b/cypress/e2e/theming/user-settings_background.cy.ts
index cdf3ef59f4d..8abcb5bace1 100644
--- a/cypress/e2e/theming/user-background.cy.ts
+++ b/cypress/e2e/theming/user-settings_background.cy.ts
@@ -1,27 +1,11 @@
/**
- * @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
-import { defaultPrimary, defaultBackground, pickRandomColor, validateBodyThemingCss } from './themingUtils'
+import { defaultPrimary, defaultBackground, validateBodyThemingCss } from './themingUtils'
+import { NavigationHeader } from '../../pages/NavigationHeader'
const admin = new User('admin', 'admin')
@@ -80,7 +64,7 @@ describe('User select shipped backgrounds and remove background', function() {
// Validate changed background and primary
cy.wait('@setBackground')
- cy.waitUntil(() => validateBodyThemingCss('#a53c17', background))
+ cy.waitUntil(() => validateBodyThemingCss('#a53c17', background, '#652e11'))
})
it('Select a bright shipped background', function() {
@@ -95,21 +79,21 @@ describe('User select shipped backgrounds and remove background', function() {
// Validate changed background and primary
cy.wait('@setBackground')
- cy.waitUntil(() => validateBodyThemingCss('#869171', background))
+ cy.waitUntil(() => validateBodyThemingCss('#56633d', background, '#dee0d3'))
})
it('Remove background', function() {
- cy.intercept('*/apps/theming/background/custom').as('clearBackground')
+ cy.intercept('*/apps/theming/background/color').as('clearBackground')
// Clear background
- cy.get('[data-user-theming-background-clear]').click()
+ cy.get('[data-user-theming-background-color]').click()
// Set the accessibility state
- cy.get('[data-user-theming-background-clear]').should('have.attr', 'aria-pressed', 'true')
+ cy.get('[data-user-theming-background-color]').should('have.attr', 'aria-pressed', 'true')
// Validate clear background
cy.wait('@clearBackground')
- cy.waitUntil(() => validateBodyThemingCss('#869171', null))
+ cy.waitUntil(() => validateBodyThemingCss('#56633d', null, '#dee0d3'))
})
})
@@ -129,18 +113,18 @@ describe('User select a custom color', function() {
it('Select a custom color', function() {
cy.intercept('*/apps/theming/background/color').as('setColor')
- pickRandomColor()
+ cy.get('[data-user-theming-background-color]').click()
+ cy.get('.color-picker__simple-color-circle').eq(5).click()
// Validate custom colour change
cy.wait('@setColor')
- cy.waitUntil(() => cy.window().then((win) => {
- const primary = getComputedStyle(win.document.body).getPropertyValue('--color-primary')
- return primary !== defaultPrimary && primary !== defaultPrimary
- }))
+ cy.waitUntil(() => validateBodyThemingCss(defaultPrimary, null, '#a5b872'))
})
})
describe('User select a bright custom color and remove background', function() {
+ const navigationHeader = new NavigationHeader()
+
before(function() {
cy.createRandomUser().then((user: User) => {
cy.login(user)
@@ -154,10 +138,11 @@ describe('User select a bright custom color and remove background', function() {
})
it('Remove background', function() {
- cy.intercept('*/apps/theming/background/custom').as('clearBackground')
+ cy.intercept('*/apps/theming/background/color').as('clearBackground')
// Clear background
- cy.get('[data-user-theming-background-clear]').click()
+ cy.get('[data-user-theming-background-color]').click()
+ cy.get('[data-user-theming-background-color]').click()
// Validate clear background
cy.wait('@clearBackground')
@@ -168,7 +153,8 @@ describe('User select a bright custom color and remove background', function() {
cy.intercept('*/apps/theming/background/color').as('setColor')
// Pick one of the bright color preset
- cy.contains('button', 'Change color').click()
+ cy.get('[data-user-theming-background-color]').scrollIntoView()
+ cy.get('[data-user-theming-background-color]').click()
cy.get('.color-picker__simple-color-circle:eq(4)').click()
// Validate custom colour change
@@ -176,12 +162,12 @@ describe('User select a bright custom color and remove background', function() {
})
it('See the header being inverted', function() {
- cy.waitUntil(() => cy.window().then((win) => {
- const firstEntry = win.document.querySelector('.app-menu-main li img')
- if (!firstEntry) {
- return false
- }
- return getComputedStyle(firstEntry).filter === 'invert(1)'
+ cy.waitUntil(() => navigationHeader.getNavigationEntries().find('img').then((el) => {
+ let ret = true
+ el.each(function() {
+ ret = ret && window.getComputedStyle(this).filter === 'invert(1)'
+ })
+ return ret
}))
})
@@ -194,16 +180,16 @@ describe('User select a bright custom color and remove background', function() {
// Validate changed background and primary
cy.wait('@setBackground')
- cy.waitUntil(() => validateBodyThemingCss('#a53c17', background))
+ cy.waitUntil(() => validateBodyThemingCss('#a53c17', background, '#652e11'))
})
it('See the header NOT being inverted this time', function() {
- cy.waitUntil(() => cy.window().then((win) => {
- const firstEntry = win.document.querySelector('.app-menu-main li')
- if (!firstEntry) {
- return false
- }
- return getComputedStyle(firstEntry).filter === 'none'
+ cy.waitUntil(() => navigationHeader.getNavigationEntries().find('img').then((el) => {
+ let ret = true
+ el.each(function() {
+ ret = ret && window.getComputedStyle(this).filter === 'none'
+ })
+ return ret
}))
})
})
@@ -240,15 +226,13 @@ describe('User select a custom background', function() {
// Wait for background to be set
cy.wait('@setBackground')
- cy.waitUntil(() => validateBodyThemingCss('#4c0c04', 'apps/theming/background?v='))
+ cy.waitUntil(() => validateBodyThemingCss(defaultPrimary, 'apps/theming/background?v=', '#2f2221'))
})
})
describe('User changes settings and reload the page', function() {
const image = 'image.jpg'
- const primaryFromImage = '#4c0c04'
-
- let selectedColor = ''
+ const colorFromImage = '#2f2221'
before(function() {
cy.createRandomUser().then((user: User) => {
@@ -280,28 +264,39 @@ describe('User changes settings and reload the page', function() {
// Wait for background to be set
cy.wait('@setBackground')
- cy.waitUntil(() => validateBodyThemingCss(primaryFromImage, 'apps/theming/background?v='))
+ cy.waitUntil(() => validateBodyThemingCss(defaultPrimary, 'apps/theming/background?v=', colorFromImage))
})
it('Select a custom color', function() {
cy.intercept('*/apps/theming/background/color').as('setColor')
- cy.contains('button', 'Change color').click()
+ cy.get('[data-user-theming-background-color]').click()
cy.get('.color-picker__simple-color-circle:eq(5)').click()
+ cy.get('[data-user-theming-background-color]').click()
// Validate clear background
cy.wait('@setColor')
- cy.waitUntil(() => cy.window().then((win) => {
- selectedColor = getComputedStyle(win.document.body).getPropertyValue('--color-primary')
- return selectedColor !== primaryFromImage
- }))
+ cy.waitUntil(() => validateBodyThemingCss(defaultPrimary, null, '#a5b872'))
+ })
+
+ it('Select a custom primary color', function() {
+ cy.intercept('/ocs/v2.php/apps/provisioning_api/api/v1/config/users/theming/primary_color').as('setPrimaryColor')
+
+ cy.get('[data-user-theming-primary-color-trigger]').scrollIntoView()
+ cy.get('[data-user-theming-primary-color-trigger]').click()
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
+ cy.wait(500)
+ cy.get('.color-picker__simple-color-circle').should('be.visible')
+ cy.get('.color-picker__simple-color-circle:eq(2)').click()
+ cy.get('[data-user-theming-primary-color-trigger]').click()
+
+ // Validate clear background
+ cy.wait('@setPrimaryColor')
+ cy.waitUntil(() => validateBodyThemingCss('#c98879', null, '#a5b872'))
})
it('Reload the page and validate persistent changes', function() {
cy.reload()
- cy.waitUntil(() => validateBodyThemingCss(selectedColor, 'apps/theming/background?v='))
-
- // validate accessibility state
- cy.get('[data-user-theming-background-custom]').should('have.attr', 'aria-pressed', 'true')
+ cy.waitUntil(() => validateBodyThemingCss('#c98879', null, '#a5b872'))
})
})