Faster theming tests, better colours comparison and properly follow admin theming changes

Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
pull/35525/head
John Molakvoæ 2 years ago
parent 8434259b1b
commit f26ee9c69d
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF

@ -39,6 +39,11 @@ return [
'url' => '/ajax/undoChanges', 'url' => '/ajax/undoChanges',
'verb' => 'POST' 'verb' => 'POST'
], ],
[
'name' => 'Theming#undoAll',
'url' => '/ajax/undoAllChanges',
'verb' => 'POST'
],
[ [
'name' => 'Theming#uploadImage', 'name' => 'Theming#uploadImage',
'url' => '/ajax/uploadImage', 'url' => '/ajax/uploadImage',

@ -275,6 +275,27 @@ class ThemingController extends Controller {
); );
} }
/**
* Revert all theming settings to their default values
* @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
*
* @return DataResponse
* @throws NotPermittedException
*/
public function undoAll(): DataResponse {
$this->themingDefaults->undoAll();
return new DataResponse(
[
'data' =>
[
'message' => $this->l10n->t('Saved'),
],
'status' => 'success'
]
);
}
/** /**
* @PublicPage * @PublicPage
* @NoCSRFRequired * @NoCSRFRequired

@ -186,6 +186,7 @@ class UserThemeController extends OCSController {
$this->backgroundService->setFileBackground($value); $this->backgroundService->setFileBackground($value);
break; break;
case BackgroundService::BACKGROUND_DEFAULT: case BackgroundService::BACKGROUND_DEFAULT:
// Delete both background and color keys
$this->backgroundService->setDefaultBackground(); $this->backgroundService->setDefaultBackground();
break; break;
default: default:

@ -160,7 +160,7 @@ class BackgroundService {
public function setDefaultBackground(): void { public function setDefaultBackground(): void {
$this->config->deleteUserValue($this->userId, Application::APP_ID, 'background_image'); $this->config->deleteUserValue($this->userId, Application::APP_ID, 'background_image');
$this->config->setUserValue($this->userId, Application::APP_ID, 'background_color', $this->themingDefaults->getDefaultColorPrimary()); $this->config->deleteUserValue($this->userId, Application::APP_ID, 'background_color');
} }
/** /**

@ -439,18 +439,26 @@ class ThemingDefaults extends \OC_Defaults {
* @param string $setting * @param string $setting
* @param string $value * @param string $value
*/ */
public function set($setting, $value) { public function set($setting, $value): void {
$this->config->setAppValue('theming', $setting, $value); $this->config->setAppValue('theming', $setting, $value);
$this->increaseCacheBuster(); $this->increaseCacheBuster();
} }
/**
* Revert all settings to the default value
*/
public function undoAll(): void {
$this->config->deleteAppValues('theming');
$this->increaseCacheBuster();
}
/** /**
* Revert settings to the default value * Revert settings to the default value
* *
* @param string $setting setting which should be reverted * @param string $setting setting which should be reverted
* @return string default value * @return string default value
*/ */
public function undo($setting) { public function undo($setting): string {
$this->config->deleteAppValue('theming', $setting); $this->config->deleteAppValue('theming', $setting);
$this->increaseCacheBuster(); $this->increaseCacheBuster();

@ -58,15 +58,15 @@
<FileInputField v-for="field in fileInputFields" <FileInputField v-for="field in fileInputFields"
:key="field.name" :key="field.name"
:aria-label="field.ariaLabel" :aria-label="field.ariaLabel"
:data-admin-theming-setting-file="field.name"
:default-mime-value="field.defaultMimeValue" :default-mime-value="field.defaultMimeValue"
:display-name="field.displayName" :display-name="field.displayName"
:mime-name="field.mimeName" :mime-name="field.mimeName"
:mime-value.sync="field.mimeValue" :mime-value.sync="field.mimeValue"
:name="field.name" :name="field.name"
data-admin-theming-setting-background
@update:theming="$emit('update:theming')" /> @update:theming="$emit('update:theming')" />
<div class="admin-theming__preview"> <div class="admin-theming__preview" data-admin-theming-preview>
<div class="admin-theming__preview-logo" /> <div class="admin-theming__preview-logo" data-admin-theming-preview-logo />
</div> </div>
</div> </div>
</NcSettingsSection> </NcSettingsSection>

@ -27,7 +27,7 @@
<NcButton type="secondary" <NcButton type="secondary"
:id="id" :id="id"
:aria-label="ariaLabel" :aria-label="ariaLabel"
data-admin-theming-setting-background-picker data-admin-theming-setting-file-picker
@click="activateLocalFilePicker"> @click="activateLocalFilePicker">
<template #icon> <template #icon>
<Upload :size="20" /> <Upload :size="20" />
@ -37,7 +37,7 @@
<NcButton v-if="showReset" <NcButton v-if="showReset"
type="tertiary" type="tertiary"
:aria-label="t('theming', 'Reset to default')" :aria-label="t('theming', 'Reset to default')"
data-admin-theming-setting-background-reset data-admin-theming-setting-file-reset
@click="undo"> @click="undo">
<template #icon> <template #icon>
<Undo :size="20" /> <Undo :size="20" />
@ -46,7 +46,7 @@
<NcButton v-if="showRemove" <NcButton v-if="showRemove"
type="tertiary" type="tertiary"
:aria-label="t('theming', 'Remove background image')" :aria-label="t('theming', 'Remove background image')"
data-admin-theming-setting-background-remove data-admin-theming-setting-file-remove
@click="removeBackground"> @click="removeBackground">
<template #icon> <template #icon>
<Delete :size="20" /> <Delete :size="20" />

@ -20,6 +20,9 @@
* *
*/ */
import { User } from '@nextcloud/cypress' import { User } from '@nextcloud/cypress'
import { colord } from 'colord'
import { pickRandomColor, validateBodyThemingCss, validateUserThemingDefaultCss } from './themingUtils'
const admin = new User('admin', 'admin') const admin = new User('admin', 'admin')
@ -28,6 +31,8 @@ const defaultBackground = 'kamil-porembinski-clouds.jpg'
describe('Admin theming settings', function() { describe('Admin theming settings', function() {
before(function() { before(function() {
// Just in case previous test failed
cy.resetTheming()
cy.login(admin) cy.login(admin)
}) })
@ -39,13 +44,16 @@ describe('Admin theming settings', function() {
it('See the default settings', function() { it('See the default settings', function() {
cy.get('[data-admin-theming-setting-primary-color-picker]').should('contain.text', defaultPrimary) cy.get('[data-admin-theming-setting-primary-color-picker]').should('contain.text', defaultPrimary)
cy.get('[data-admin-theming-setting-primary-color-reset]').should('not.exist') cy.get('[data-admin-theming-setting-primary-color-reset]').should('not.exist')
cy.get('[data-admin-theming-setting-background-reset]').should('not.exist') cy.get('[data-admin-theming-setting-file-reset]').should('not.exist')
cy.get('[data-admin-theming-setting-background-remove]').should('be.visible') cy.get('[data-admin-theming-setting-file-remove]').should('be.visible')
}) })
}) })
describe('Change the primary colour', function() { describe('Change the primary colour and reset it', function() {
let selectedColor = ''
before(function() { before(function() {
// Just in case previous test failed
cy.resetTheming()
cy.login(admin) cy.login(admin)
}) })
@ -57,14 +65,11 @@ describe('Change the primary colour', function() {
it('Change the primary colour', function() { it('Change the primary colour', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor') cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
cy.get('[data-admin-theming-setting-primary-color-picker]').click() pickRandomColor('[data-admin-theming-setting-primary-color-picker]')
cy.get('.color-picker__simple-color-circle:eq(3)').click() .then(color => selectedColor = color)
cy.wait('@setColor') cy.wait('@setColor')
cy.waitUntil(() => cy.window().then((win) => { cy.waitUntil(() => validateBodyThemingCss(selectedColor, defaultBackground))
const primary = getComputedStyle(win.document.body).getPropertyValue('--color-primary-default')
return primary !== defaultPrimary
}))
}) })
it('Screenshot the login page', function() { it('Screenshot the login page', function() {
@ -73,32 +78,21 @@ describe('Change the primary colour', function() {
cy.screenshot() cy.screenshot()
}) })
it('Login again and go to the admin theming section', function() { it('Undo theming settings', function() {
cy.login(admin) cy.resetTheming()
cy.visit('/settings/admin/theming')
})
it('Reset the primary colour', function() {
cy.intercept('*/apps/theming/ajax/undoChanges').as('undoChanges')
cy.get('[data-admin-theming-setting-primary-color-reset]').click()
cy.wait('@undoChanges')
cy.waitUntil(() => cy.window().then((win) => {
const primary = getComputedStyle(win.document.body).getPropertyValue('--color-primary-default')
return primary === defaultPrimary
}))
}) })
it('Screenshot the login page', function() { it('Screenshot the login page', function() {
cy.logout()
cy.visit('/') cy.visit('/')
cy.waitUntil(validateBodyThemingCss)
cy.screenshot() cy.screenshot()
}) })
}) })
describe('Remove the default background', function() { describe('Remove the default background and restore it', function() {
before(function() { before(function() {
// Just in case previous test failed
cy.resetTheming()
cy.login(admin) cy.login(admin)
}) })
@ -110,13 +104,13 @@ describe('Remove the default background', function() {
it('Remove the default background', function() { 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-background-remove]').click() cy.get('[data-admin-theming-setting-file-remove]').click()
cy.wait('@removeBackground') cy.wait('@removeBackground')
cy.waitUntil(() => cy.window().then((win) => { cy.waitUntil(() => cy.window().then((win) => {
const backgroundDefault = getComputedStyle(win.document.body).getPropertyValue('--image-background-default') const currentBackgroundDefault = getComputedStyle(win.document.body).getPropertyValue('--image-background-default')
const backgroundPlain = getComputedStyle(win.document.body).getPropertyValue('--image-background-plain') const backgroundPlain = getComputedStyle(win.document.body).getPropertyValue('--image-background-plain')
return !backgroundDefault.includes(defaultBackground) return !currentBackgroundDefault.includes(defaultBackground)
&& backgroundPlain !== '' && backgroundPlain !== ''
})) }))
}) })
@ -127,38 +121,25 @@ describe('Remove the default background', function() {
cy.screenshot() cy.screenshot()
}) })
it('Login again and go to the admin theming section', function() { it('Undo theming settings', function() {
cy.login(admin) cy.resetTheming()
cy.visit('/settings/admin/theming')
})
it('Restore the default background', function() {
cy.intercept('*/apps/theming/ajax/undoChanges').as('undoChanges')
cy.get('[data-admin-theming-setting-background-reset]').click()
cy.wait('@undoChanges')
cy.waitUntil(() => cy.window().then((win) => {
const backgroundDefault = getComputedStyle(win.document.body).getPropertyValue('--image-background-default')
const backgroundPlain = getComputedStyle(win.document.body).getPropertyValue('--image-background-plain')
return backgroundDefault.includes(defaultBackground)
&& backgroundPlain === ''
}))
}) })
it('Screenshot the login page', function() { it('Screenshot the login page', function() {
cy.logout()
cy.visit('/') cy.visit('/')
cy.waitUntil(validateBodyThemingCss)
cy.screenshot() cy.screenshot()
}) })
}) })
describe('Change the login fields', function() { describe('Change the login fields then reset them', function() {
const name = 'ABCdef123' const name = 'ABCdef123'
const url = 'https://example.com' const url = 'https://example.com'
const slogan = 'Testing is fun' const slogan = 'Testing is fun'
before(function() { before(function() {
// Just in case previous test failed
cy.resetTheming()
cy.login(admin) cy.login(admin)
}) })
@ -214,29 +195,11 @@ describe('Change the login fields', function() {
cy.get('footer p').should('contain.text', ` ${slogan}`) cy.get('footer p').should('contain.text', ` ${slogan}`)
}) })
it('Login again and go to the admin theming section', function() { it('Undo theming settings', function() {
cy.login(admin) cy.resetTheming()
cy.visit('/settings/admin/theming')
})
it('Undo changes', function() {
cy.intercept('*/apps/theming/ajax/undoChanges').as('undoChanges')
cy.get('[data-admin-theming-setting-field="name"] .input-field__clear-button')
.scrollIntoView().click()
cy.wait('@undoChanges')
cy.get('[data-admin-theming-setting-field="url"] .input-field__clear-button')
.scrollIntoView().click()
cy.wait('@undoChanges')
cy.get('[data-admin-theming-setting-field="slogan"] .input-field__clear-button')
.scrollIntoView().click()
cy.wait('@undoChanges')
}) })
it('Check login screen changes', function() { it('Check login screen changes', function() {
cy.logout()
cy.visit('/') cy.visit('/')
cy.get('[data-login-form-headline]').should('not.contain.text', name) cy.get('[data-login-form-headline]').should('not.contain.text', name)
@ -246,8 +209,10 @@ describe('Change the login fields', function() {
}) })
}) })
describe('Disable user theming', function() { describe('Disable user theming and enable it back', function() {
before(function() { before(function() {
// Just in case previous test failed
cy.resetTheming()
cy.login(admin) cy.login(admin)
}) })
@ -278,25 +243,74 @@ describe('Disable user theming', function() {
cy.visit('/settings/user/theming') cy.visit('/settings/user/theming')
cy.get('[data-user-theming-background-disabled]').scrollIntoView().should('be.visible') cy.get('[data-user-theming-background-disabled]').scrollIntoView().should('be.visible')
}) })
})
it('Login back as admin', function() { describe('User default option matches admin theming', function() {
cy.logout() let selectedColor = ''
before(function() {
// Just in case previous test failed
cy.resetTheming()
cy.login(admin) cy.login(admin)
}) })
after(function() {
cy.resetTheming()
})
it('See the admin theming section', function() { it('See the admin theming section', function() {
cy.visit('/settings/admin/theming') cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible') cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
}) })
it('Enable back user theming', function() { it('Change the primary colour', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('enableUserTheming') cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
cy.get('[data-admin-theming-setting-disable-user-theming]') pickRandomColor('[data-admin-theming-setting-primary-color-picker]')
.scrollIntoView().should('be.visible') .then(color => selectedColor = color)
cy.get('[data-admin-theming-setting-disable-user-theming] input[type="checkbox"]').uncheck({ force: true })
cy.get('[data-admin-theming-setting-disable-user-theming] input[type="checkbox"]').should('not.be.checked') cy.wait('@setColor')
cy.waitUntil(() => cy.window().then((win) => {
const primary = getComputedStyle(win.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.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=')
}))
})
it('Logout and check changes', function() {
cy.logout()
cy.visit('/')
cy.waitUntil(() => validateBodyThemingCss(selectedColor, '/apps/theming/image/background?v='))
})
it('Login as user', function() {
cy.createRandomUser().then((user) => {
cy.login(user)
})
})
it('See the user background settings', function() {
cy.visit('/settings/user/theming')
cy.get('[data-user-theming-background-settings]').scrollIntoView().should('be.visible')
})
it('See the default background option selected', function() {
cy.get('[data-user-theming-background-default]').should('be.visible')
cy.get('[data-user-theming-background-default]').should('have.class', 'background--active')
cy.wait('@enableUserTheming') cy.waitUntil(() => validateUserThemingDefaultCss(selectedColor, '/apps/theming/image/background?v='))
}) })
}) })

@ -0,0 +1,75 @@
/**
* @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/>.
*
*/
import { colord } from 'colord'
/**
* Validate the current page body css variables
*
* @param {string} expectedColor the expected color
* @param {string} expectedBackground the expected background
*/
export const validateBodyThemingCss = function(expectedColor = '#0082c9', expectedBackground = 'kamil-porembinski-clouds.jpg') {
return cy.window().then((win) => {
const guestBackgroundColor = getComputedStyle(win.document.body).backgroundColor
const guestBackgroundImage = getComputedStyle(win.document.body).backgroundImage
return colord(guestBackgroundColor).isEqual(expectedColor)
&& guestBackgroundImage.includes(expectedBackground)
})
}
/**
* Validate the user theming default select option css
*
* @param {string} expectedColor the expected color
* @param {string} expectedBackground the expected background
*/
export const validateUserThemingDefaultCss = function(expectedColor = '#0082c9', expectedBackground = 'kamil-porembinski-clouds.jpg') {
return cy.window().then((win) => {
const defaultSelectButton = win.document.querySelector('[data-user-theming-background-default]')
const customColorSelectButton = win.document.querySelector('[data-user-theming-background-color]')
if (!defaultSelectButton || !customColorSelectButton) {
return false
}
const defaultOptionBackground = getComputedStyle(defaultSelectButton).backgroundImage
const defaultOptionBorderColor = getComputedStyle(defaultSelectButton).borderColor
const colorPickerOptionColor = getComputedStyle(customColorSelectButton).backgroundColor
return defaultOptionBackground.includes(expectedBackground)
&& colord(defaultOptionBorderColor).isEqual(expectedColor)
&& colord(colorPickerOptionColor).isEqual(expectedColor)
})
}
export const pickRandomColor = function(pickerSelector: string): Cypress.Chainable<string> {
// Pick one of the first 8 options
const randColour = Math.floor(Math.random() * 8)
// Open picker
cy.get(pickerSelector).click()
// Return selected colour
return cy.get(pickerSelector).get(`.color-picker__simple-color-circle`).eq(randColour)
.click().then(colorElement => {
const selectedColor = colorElement.css('background-color')
return selectedColor
})
}

@ -23,16 +23,17 @@ import type { User } from '@nextcloud/cypress'
const defaultPrimary = '#006aa3' const defaultPrimary = '#006aa3'
const defaultBackground = 'kamil-porembinski-clouds.jpg' const defaultBackground = 'kamil-porembinski-clouds.jpg'
import { colord } from 'colord'
const validateThemingCss = function(expectedPrimary = '#0082c9', expectedBackground = 'kamil-porembinski-clouds.jpg', bright = false) { const validateThemingCss = function(expectedPrimary = '#0082c9', expectedBackground = 'kamil-porembinski-clouds.jpg', bright = false) {
return cy.window().then((win) => { return cy.window().then((win) => {
const primary = getComputedStyle(win.document.body).getPropertyValue('--color-primary') const backgroundColor = getComputedStyle(win.document.body).backgroundColor
const background = getComputedStyle(win.document.body).getPropertyValue('--image-background') const backgroundImage = getComputedStyle(win.document.body).backgroundImage
const invertIfBright = getComputedStyle(win.document.body).getPropertyValue('--background-image-invert-if-bright') const invertIfBright = getComputedStyle(win.document.body).getPropertyValue('--background-image-invert-if-bright')
// Returning boolean for cy.waitUntil usage // Returning boolean for cy.waitUntil usage
return primary === expectedPrimary return colord(backgroundColor).isEqual(expectedPrimary)
&& background.includes(expectedBackground) && backgroundImage.includes(expectedBackground)
&& invertIfBright === (bright ? 'invert(100%)' : 'no') && invertIfBright === (bright ? 'invert(100%)' : 'no')
}) })
} }
@ -163,7 +164,6 @@ describe('User select a custom background', function() {
}) })
}) })
describe('User changes settings and reload the page', function() { describe('User changes settings and reload the page', function() {
const image = 'image.jpg' const image = 'image.jpg'
const primaryFromImage = '#4c0c04' const primaryFromImage = '#4c0c04'

@ -21,7 +21,7 @@
*/ */
/* eslint-disable node/no-unpublished-import */ /* eslint-disable node/no-unpublished-import */
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { addCommands, type User} from '@nextcloud/cypress' import { addCommands, User } from '@nextcloud/cypress'
import { basename } from 'path' import { basename } from 'path'
// Add custom commands // Add custom commands
@ -33,7 +33,8 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress { namespace Cypress {
interface Chainable<Subject = any> { interface Chainable<Subject = any> {
uploadFile(user: User, fixture: string, mimeType: string, target ?: string): Cypress.Chainable<void> uploadFile(user: User, fixture?: string, mimeType?: string, target ?: string): Cypress.Chainable<void>,
resetTheming(): Cypress.Chainable<void>,
} }
} }
} }
@ -50,7 +51,7 @@ Cypress.env('baseUrl', url)
* @param {string} mimeType e.g. image/png * @param {string} mimeType e.g. image/png
* @param {string} [target] the target of the file relative to the user root * @param {string} [target] the target of the file relative to the user root
*/ */
Cypress.Commands.add('uploadFile', (user, fixture, mimeType, target = `/${fixture}`) => { Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'image/jpeg', target = `/${fixture}`) => {
cy.clearCookies() cy.clearCookies()
const fileName = basename(target) const fileName = basename(target)
@ -84,3 +85,29 @@ Cypress.Commands.add('uploadFile', (user, fixture, mimeType, target = `/${fixtur
} }
}) })
}) })
/**
* Reset the admin theming entirely
*/
Cypress.Commands.add('resetTheming', () => {
const admin = new User('admin', 'admin')
cy.clearCookies()
cy.login(admin)
// Clear all settings
cy.request('/csrftoken').then(({ body }) => {
const requestToken = body.token
axios({
method: 'POST',
url: '/index.php/apps/theming/ajax/undoAllChanges',
headers: {
'requesttoken': requestToken,
},
})
})
// Clear admin session
cy.clearCookies()
})

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

17
package-lock.json generated

@ -39,6 +39,7 @@
"buffer": "^6.0.3", "buffer": "^6.0.3",
"camelcase": "^6.3.0", "camelcase": "^6.3.0",
"clipboard": "^2.0.10", "clipboard": "^2.0.10",
"colord": "^2.9.3",
"core-js": "^3.24.0", "core-js": "^3.24.0",
"davclient.js": "git+https://github.com/owncloud/davclient.js.git#0.2.1", "davclient.js": "git+https://github.com/owncloud/davclient.js.git#0.2.1",
"debounce": "^1.2.1", "debounce": "^1.2.1",
@ -8245,11 +8246,9 @@
} }
}, },
"node_modules/colord": { "node_modules/colord": {
"version": "2.9.2", "version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
"integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
"dev": true,
"peer": true
}, },
"node_modules/colorette": { "node_modules/colorette": {
"version": "2.0.16", "version": "2.0.16",
@ -30099,11 +30098,9 @@
"peer": true "peer": true
}, },
"colord": { "colord": {
"version": "2.9.2", "version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
"integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
"dev": true,
"peer": true
}, },
"colorette": { "colorette": {
"version": "2.0.16", "version": "2.0.16",

@ -62,6 +62,7 @@
"buffer": "^6.0.3", "buffer": "^6.0.3",
"camelcase": "^6.3.0", "camelcase": "^6.3.0",
"clipboard": "^2.0.10", "clipboard": "^2.0.10",
"colord": "^2.9.3",
"core-js": "^3.24.0", "core-js": "^3.24.0",
"davclient.js": "git+https://github.com/owncloud/davclient.js.git#0.2.1", "davclient.js": "git+https://github.com/owncloud/davclient.js.git#0.2.1",
"debounce": "^1.2.1", "debounce": "^1.2.1",

Loading…
Cancel
Save