More theming cypress tests and some fixes

Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ 2022-11-29 09:51:34 +01:00
parent 064fa10ecf
commit a869259e41
No known key found for this signature in database
GPG key ID: 60C25B8C072916CF
27 changed files with 416 additions and 79 deletions

View file

@ -94,7 +94,7 @@ class ImageManager {
case 'favicon':
return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
case 'background':
return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND);
return $this->urlGenerator->linkTo(Application::APP_ID, 'img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
}
return '';
}

View file

@ -27,7 +27,6 @@ declare(strict_types=1);
namespace OCA\Theming\Jobs;
use OCA\Theming\AppInfo\Application;
use OCP\App\IAppManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\QueuedJob;

View file

@ -93,7 +93,7 @@ class BeforeTemplateRenderedListener implements IEventListener {
/** User color */
$this->initialState->provideInitialState(
'backgroundColor',
$this->config->getUserValue($userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
$this->config->getUserValue($userId, Application::APP_ID, 'background_color', BackgroundService::DEFAULT_COLOR),
);
/**
@ -106,7 +106,7 @@ class BeforeTemplateRenderedListener implements IEventListener {
);
$this->initialState->provideInitialState(
'defaultShippedBackground',
BackgroundService::DEFAULT_BACKGROUND,
BackgroundService::DEFAULT_BACKGROUND_IMAGE,
);
/** List of all shipped backgrounds */

View file

@ -53,7 +53,7 @@ class BackgroundService {
public const BACKGROUND_DEFAULT = 'default';
public const BACKGROUND_DISABLED = 'disabled';
public const DEFAULT_BACKGROUND = 'kamil-porembinski-clouds.jpg';
public const DEFAULT_BACKGROUND_IMAGE = 'kamil-porembinski-clouds.jpg';
public const SHIPPED_BACKGROUNDS = [
'anatoly-mikhaltsov-butterfly-wing-scale.jpg' => [
'attribution' => 'Butterfly wing scale (Anatoly Mikhaltsov, CC BY-SA)',

View file

@ -89,16 +89,14 @@ trait CommonThemeTrait {
$variables = [];
// Default last fallback values
$variables['--image-background-default'] = $backgroundDeleted ?: "url('" . $this->themingDefaults->getBackground() . "')";
$variables['--image-background-default'] = "url('" . $this->themingDefaults->getBackground() . "')";
$variables['--color-background-plain'] = $this->defaultPrimaryColor;
// If primary as background has been request or if we have a custom primary colour
// let's not define the background image
if ($backgroundDeleted) {
$variables['--color-background-plain'] = $this->themingDefaults->getColorPrimary();
if ($this->themingDefaults->isUserThemingDisabled() || $user === null) {
$variables['--image-background-plain'] = 'yes';
}
$variables['--image-background-plain'] = 'yes';
}
// Register image variables only if custom-defined
@ -111,7 +109,6 @@ trait CommonThemeTrait {
continue;
}
$variables['--image-background-size'] = 'cover';
$variables['--image-background-default'] = "url('" . $imageUrl . "')";
}
// --image-background is overridden by user theming
$variables["--image-$image"] = "url('" . $imageUrl . "')";

View file

@ -227,10 +227,10 @@ class ThemingDefaults extends \OC_Defaults {
// user-defined primary color
if (!empty($user)) {
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_color', '');
$themingBackgroundColor = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background_color', '');
// If the user selected a specific colour
if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
return $themingBackground;
if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackgroundColor)) {
return $themingBackgroundColor;
}
}

View file

@ -24,36 +24,46 @@
<section>
<NcSettingsSection :title="t('theming', 'Theming')"
:description="t('theming', 'Theming makes it possible to easily customize the look and feel of your instance and supported clients. This will be visible for all users.')"
:doc-url="docUrl">
:doc-url="docUrl"
data-admin-theming-settings>
<div class="admin-theming">
<NcNoteCard v-if="!isThemable"
type="error"
:show-alert="true">
<p>{{ notThemableErrorMessage }}</p>
</NcNoteCard>
<!-- Name, web link, slogan... fields -->
<TextField v-for="field in textFields"
:key="field.name"
:name="field.name"
:value.sync="field.value"
:data-admin-theming-setting-field="field.name"
:default-value="field.defaultValue"
:type="field.type"
:display-name="field.displayName"
:placeholder="field.placeholder"
:maxlength="field.maxlength"
:name="field.name"
:placeholder="field.placeholder"
:type="field.type"
:value.sync="field.value"
@update:theming="$emit('update:theming')" />
<!-- Primary color picker -->
<ColorPickerField :name="colorPickerField.name"
:value.sync="colorPickerField.value"
:default-value="colorPickerField.defaultValue"
:display-name="colorPickerField.displayName"
:value.sync="colorPickerField.value"
data-admin-theming-setting-primary-color
@update:theming="$emit('update:theming')" />
<!-- Default background picker -->
<FileInputField v-for="field in fileInputFields"
:key="field.name"
:name="field.name"
:mime-name="field.mimeName"
:mime-value.sync="field.mimeValue"
:aria-label="field.ariaLabel"
:default-mime-value="field.defaultMimeValue"
:display-name="field.displayName"
:aria-label="field.ariaLabel"
:mime-name="field.mimeName"
:mime-value.sync="field.mimeValue"
:name="field.name"
data-admin-theming-setting-background
@update:theming="$emit('update:theming')" />
<div class="admin-theming__preview">
<div class="admin-theming__preview-logo" />
@ -87,6 +97,7 @@
:display-name="userThemingField.displayName"
:label="userThemingField.label"
:description="userThemingField.description"
data-admin-theming-setting-disable-user-theming
@update:theming="$emit('update:theming')" />
<a v-if="!canThemeIcons"
:href="docUrlIcons"

View file

@ -63,7 +63,8 @@
</NcSettingsSection>
<NcSettingsSection :title="t('theming', 'Background')"
class="background">
class="background"
data-user-theming-background-disabled>
<template v-if="isUserThemingDisabled">
<p>{{ t('theming', 'Customization has been disabled by your administrator') }}</p>
</template>

View file

@ -99,7 +99,6 @@ import { Palette } from 'node-vibrant/lib/color'
import { getFilePickerBuilder } from '@nextcloud/dialogs'
import { getCurrentUser } from '@nextcloud/auth'
const backgroundColor = loadState('theming', 'backgroundColor')
const backgroundImage = loadState('theming', 'backgroundImage')
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
const themingDefaultBackground = loadState('theming', 'themingDefaultBackground')
@ -132,7 +131,6 @@ export default {
// User background image and color settings
backgroundImage,
backgroundColor,
}
},
@ -210,7 +208,6 @@ export default {
async update(data) {
// Update state
this.backgroundImage = data.backgroundImage
this.backgroundColor = data.backgroundColor
this.Theming.color = data.backgroundColor
// Notify parent and reload style

View file

@ -30,13 +30,15 @@
<NcButton class="field__button"
type="primary"
:id="id"
:aria-label="t('theming', 'Select a custom color')">
:aria-label="t('theming', 'Select a custom color')"
data-admin-theming-setting-primary-color-picker>
{{ value }}
</NcButton>
</NcColorPicker>
<NcButton v-if="value !== defaultValue"
type="tertiary"
:aria-label="t('theming', 'Reset to default')"
data-admin-theming-setting-primary-color-reset
@click="undo">
<template #icon>
<Undo :size="20" />

View file

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

View file

@ -85,7 +85,7 @@ class DefaultThemeTest extends TestCase {
$this->themingDefaults
->expects($this->any())
->method('getBackground')
->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND);
->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE);
$this->l10n
->expects($this->any())

View file

@ -464,7 +464,7 @@ class ThemingDefaultsTest extends TestCase {
$this->config
->expects($this->once())
->method('getUserValue')
->with('user', 'theming', 'background')
->with('user', 'theming', 'background_color')
->willReturn('');
$this->assertEquals(BackgroundService::DEFAULT_COLOR, $this->template->getColorPrimary());

View file

@ -64,6 +64,8 @@ $expectedFiles = [
'COPYING',
'core',
'cron.php',
'cypress.config.ts',
'cypress',
'dist',
'index.html',
'index.php',
@ -87,6 +89,7 @@ $expectedFiles = [
'status.php',
'tests',
'themes',
'tsconfig.json',
'vendor-bin',
'version.php',
'webpack.common.js',

View file

@ -26,7 +26,7 @@
name="login"
:action="loginActionUrl"
@submit="submit">
<fieldset class="login-form__fieldset">
<fieldset class="login-form__fieldset" data-login-form>
<NcNoteCard v-if="apacheAuthFailed"
:title="t('core', 'Server side authentication failed!')"
type="warning">
@ -52,7 +52,7 @@
<!-- the following div ensures that the spinner is always inside the #message div -->
<div style="clear: both;" />
</div>
<h2 class="login-form__headline" v-html="headline" />
<h2 class="login-form__headline" data-login-form-headline v-html="headline" />
<NcTextField id="user"
ref="user"
:label="t('core', 'Account name or email')"
@ -64,6 +64,7 @@
:spellchecking="false"
:autocomplete="autoCompleteAllowed ? 'username' : 'off'"
required
data-login-form-input-user
@change="updateUsername" />
<NcPasswordField id="password"
@ -78,9 +79,10 @@
:label="t('core', 'Password')"
:helper-text="errorLabel"
:error="isError"
data-login-form-input-password
required />
<LoginButton :loading="loading" />
<LoginButton data-login-form-submit :loading="loading" />
<input v-if="redirectUrl"
type="hidden"

View file

@ -0,0 +1,302 @@
/**
* @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 { User } from '@nextcloud/cypress'
const admin = new User('admin', 'admin')
const defaultPrimary = '#0082c9'
const defaultBackground = 'kamil-porembinski-clouds.jpg'
describe('Admin theming settings', function() {
before(function() {
cy.login(admin)
})
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
})
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-reset]').should('not.exist')
cy.get('[data-admin-theming-setting-background-reset]').should('not.exist')
cy.get('[data-admin-theming-setting-background-remove]').should('be.visible')
})
})
describe('Change the primary colour', function() {
before(function() {
cy.login(admin)
})
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
})
it('Change the primary colour', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('setColor')
cy.get('[data-admin-theming-setting-primary-color-picker]').click()
cy.get('.color-picker__simple-color-circle:eq(3)').click()
cy.wait('@setColor')
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() {
cy.logout()
cy.visit('/')
cy.screenshot()
})
it('Login again and go to the admin theming section', function() {
cy.login(admin)
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() {
cy.logout()
cy.visit('/')
cy.screenshot()
})
})
describe('Remove the default background', function() {
before(function() {
cy.login(admin)
})
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
})
it('Remove the default background', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('removeBackground')
cy.get('[data-admin-theming-setting-background-remove]').click()
cy.wait('@removeBackground')
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() {
cy.logout()
cy.visit('/')
cy.screenshot()
})
it('Login again and go to the admin theming section', function() {
cy.login(admin)
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() {
cy.logout()
cy.visit('/')
cy.screenshot()
})
})
describe('Change the login fields', function() {
const name = 'ABCdef123'
const url = 'https://example.com'
const slogan = 'Testing is fun'
before(function() {
cy.login(admin)
})
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
})
it('Change the name field', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('updateFields')
// Name
cy.get('[data-admin-theming-setting-field="name"] input[type="text"]')
.scrollIntoView()
.type('{selectall}')
.type(name)
.type('{enter}')
cy.wait('@updateFields')
// Url
cy.get('[data-admin-theming-setting-field="url"] input[type="url"]')
.scrollIntoView()
.type('{selectall}')
.type(url)
.type('{enter}')
cy.wait('@updateFields')
// Slogan
cy.get('[data-admin-theming-setting-field="slogan"] input[type="text"]')
.scrollIntoView()
.type('{selectall}')
.type(slogan)
.type('{enter}')
cy.wait('@updateFields')
})
it('Ensure undo button presence', function() {
cy.get('[data-admin-theming-setting-field="name"] .input-field__clear-button')
.scrollIntoView().should('be.visible')
cy.get('[data-admin-theming-setting-field="url"] .input-field__clear-button')
.scrollIntoView().should('be.visible')
cy.get('[data-admin-theming-setting-field="slogan"] .input-field__clear-button')
.scrollIntoView().should('be.visible')
})
it('Check login screen changes', function() {
cy.logout()
cy.visit('/')
cy.get('[data-login-form-headline]').should('contain.text', name)
cy.get('footer p a').should('have.text', name)
cy.get('footer p a').should('have.attr', 'href', url)
cy.get('footer p').should('contain.text', ` ${slogan}`)
})
it('Login again and go to the admin theming section', function() {
cy.login(admin)
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() {
cy.logout()
cy.visit('/')
cy.get('[data-login-form-headline]').should('not.contain.text', name)
cy.get('footer p a').should('not.have.text', name)
cy.get('footer p a').should('not.have.attr', 'href', url)
cy.get('footer p').should('not.contain.text', ` ${slogan}`)
})
})
describe('Disable user theming', function() {
before(function() {
cy.login(admin)
})
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
})
it('Disable user theming', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('disableUserTheming')
cy.get('[data-admin-theming-setting-disable-user-theming]')
.scrollIntoView().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')
})
it('Login as user', function() {
cy.logout()
cy.createRandomUser().then((user) => {
cy.login(user)
})
})
it('See the user disabled background settings', function() {
cy.visit('/settings/user/theming')
cy.get('[data-user-theming-background-disabled]').scrollIntoView().should('be.visible')
})
it('Login back as admin', function() {
cy.logout()
cy.login(admin)
})
it('See the admin theming section', function() {
cy.visit('/settings/admin/theming')
cy.get('[data-admin-theming-settings]').scrollIntoView().should('be.visible')
})
it('Enable back user theming', function() {
cy.intercept('*/apps/theming/ajax/updateStylesheet').as('enableUserTheming')
cy.get('[data-admin-theming-setting-disable-user-theming]')
.scrollIntoView().should('be.visible')
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('@enableUserTheming')
})
})

View file

@ -162,3 +162,55 @@ describe('User select a custom background', function() {
cy.waitUntil(() => validateThemingCss('#4c0c04', 'apps/theming/background?v='))
})
})
describe('User changes settings and reload the page', function() {
const image = 'image.jpg'
const primaryFromImage = '#4c0c04'
let selectedColor = ''
before(function() {
cy.createRandomUser().then((user: User) => {
cy.uploadFile(user, image, 'image/jpeg')
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('Select a custom background', function() {
cy.intercept('*/apps/theming/background/custom').as('setBackground')
// Pick background
cy.get('[data-user-theming-background-custom]').click()
cy.get(`#picker-filestable tr[data-entryname="${image}"]`).click()
cy.get('#oc-dialog-filepicker-content ~ .oc-dialog-buttonrow button.primary').click()
// Wait for background to be set
cy.wait('@setBackground')
cy.waitUntil(() => validateThemingCss(primaryFromImage, 'apps/theming/background?v='))
})
it('Select a custom color', function() {
cy.intercept('*/apps/theming/background/color').as('setColor')
cy.get('[data-user-theming-background-color]').click()
cy.get('.color-picker__simple-color-circle:eq(5)').click()
// Validate clear background
cy.wait('@setColor')
cy.waitUntil(() => cy.window().then((win) => {
selectedColor = getComputedStyle(win.document.body).getPropertyValue('--color-primary')
return selectedColor !== primaryFromImage
}))
})
it('Reload the page and validate persistent changes', function() {
cy.reload()
cy.waitUntil(() => validateThemingCss(selectedColor, 'apps/theming/background?v='))
})
})

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

View file

@ -400,6 +400,8 @@
/*! https://mths.be/he v1.2.0 by @mathias | MIT license */
/*! https://mths.be/punycode v1.3.2 by @mathias */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! jQuery Migrate v3.4.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */

File diff suppressed because one or more lines are too long

4
dist/core-login.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,34 +0,0 @@
@apache
Feature: app-theming
# FIXME test with cypress
# The existing DOM testing framework used here is not fully suitable for testing UIs implemented with modern frontend frameworks like Vue
Scenario: changing the color updates the primary color
Given I am logged in as the admin
And I visit the admin settings page
And I open the "Theming" section
# And I see that the color selector in the Theming app has loaded
# The "eventually" part is not really needed here, as the colour is not
# being animated at this point, but there is no need to create a specific
# step just for this.
# And I see that the primary color is eventually "#00639a"
# And I see that the non-plain background color variable is eventually "#0082c9"
# When I set the "Color" parameter in the Theming app to "#C9C9C9"
# Then I see that the parameters in the Theming app are eventually saved
# And I see that the primary color is eventually "#00639a"
# And I see that the non-plain background color variable is eventually "#C9C9C9"
Scenario: resetting the color updates the primary color
Given I am logged in as the admin
And I visit the admin settings page
And I open the "Theming" section
# And I see that the color selector in the Theming app has loaded
# And I set the "Color" parameter in the Theming app to "#C9C9C9"
# And I see that the parameters in the Theming app are eventually saved
# And I see that the primary color is eventually "#00639a"
# And I see that the non-plain background color variable is eventually "#C9C9C9"
# When I reset the "Color" parameter in the Theming app to its default value
# Then I see that the parameters in the Theming app are eventually saved
# And I see that the primary color is eventually "#00639a"
# And I see that the non-plain background color variable is eventually "#0082c9"