Explorar el Código

fix(tests): Adjust theming test for new splitted background and primary colors

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
pull/42977/head
Ferdinand Thiessen hace 4 meses
padre
commit
538a04968a
No account linked to committer's email address

+ 3
- 3
apps/settings/src/components/AdminAI.vue Ver fichero

@@ -190,9 +190,9 @@ export default {

.draggable__number {
border-radius: 20px;
border: 2px solid var(--color-primary-default);
color: var(--color-primary-default);
padding: 0px 7px;
border: 2px solid var(--color-primary-element);
color: var(--color-primary-element);
padding: 0px 7px;
margin-right: 3px;
}


+ 1
- 1
apps/theming/css/default.css Ver fichero

@@ -71,7 +71,6 @@
--primary-invert-if-bright: no;
--primary-invert-if-dark: invert(100%);
--color-primary: #00679e;
--color-primary-default: #0082c9;
--color-primary-text: #ffffff;
--color-primary-hover: #3285b1;
--color-primary-light: #e5eff5;
@@ -87,4 +86,5 @@
--gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%);
--color-background-plain: #00679e;
--color-background-plain-text: #ffffff;
--image-background: url('/apps/theming/img/background/kamil-porembinski-clouds.jpg');
}

+ 3
- 2
apps/theming/lib/Service/BackgroundService.php Ver fichero

@@ -43,7 +43,7 @@ use OCP\Lock\LockedException;
use OCP\PreConditionNotMetException;

class BackgroundService {
public const DEFAULT_COLOR = '#0082c9';
public const DEFAULT_COLOR = '#00679e';
public const DEFAULT_BACKGROUND_COLOR = '#00679e';

/**
@@ -300,9 +300,10 @@ class BackgroundService {
$meanColor = $this->calculateMeanColor($image);
if ($meanColor !== false) {
$this->config->setAppValue(Application::APP_ID, 'background_color', $meanColor);
return $meanColor;
}
return $meanColor;
}
return null;
}

/**

+ 1
- 2
apps/theming/lib/Themes/CommonThemeTrait.php Ver fichero

@@ -61,7 +61,6 @@ trait CommonThemeTrait {
'--primary-invert-if-dark' => $this->util->invertTextColor($colorPrimaryElement) ? 'no' : 'invert(100%)',

'--color-primary' => $this->primaryColor,
'--color-primary-default' => $this->defaultPrimaryColor,
'--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
'--color-primary-light' => $colorPrimaryLight,
@@ -105,7 +104,7 @@ trait CommonThemeTrait {
if ($this->imageManager->hasImage($image)) {
$imageUrl = $this->imageManager->getImageUrl($image);
$variables["--image-$image"] = "url('" . $imageUrl . "')";
} else if ($image === 'background') {
} elseif ($image === 'background') {
// Apply default background if nothing is configured
$variables['--image-background'] = "url('" . $this->themingDefaults->getBackground() . "')";
}

+ 1
- 1
apps/theming/lib/Util.php Ver fichero

@@ -93,7 +93,7 @@ class Util {
$contrast = $this->colorContrast($color, $blurredBackground);

// Min. element contrast is 3:1 but we need to keep hover states in mind -> min 3.2:1
$minContrast = $highContrast ? 5.5 : 3.2;
$minContrast = $highContrast ? 5.6 : 3.2;

while ($contrast < $minContrast && $iteration++ < 100) {
$hsl = Color::hexToHsl($color);

+ 4
- 0
apps/theming/openapi.json Ver fichero

@@ -63,6 +63,7 @@
"color-element-dark",
"logo",
"background",
"background-text",
"background-plain",
"background-default",
"logoheader",
@@ -99,6 +100,9 @@
"background": {
"type": "string"
},
"background-text": {
"type": "string"
},
"background-plain": {
"type": "boolean"
},

+ 10
- 4
apps/theming/tests/Settings/PersonalTest.php Ver fichero

@@ -30,6 +30,7 @@ namespace OCA\Theming\Tests\Settings;
use OCA\Theming\AppInfo\Application;
use OCA\Theming\ImageManager;
use OCA\Theming\ITheme;
use OCA\Theming\Service\BackgroundService;
use OCA\Theming\Service\ThemesService;
use OCA\Theming\Settings\Personal;
use OCA\Theming\Themes\DarkHighContrastTheme;
@@ -116,18 +117,23 @@ class PersonalTest extends TestCase {
->with('enforce_theme', '')
->willReturn($enforcedTheme);

$this->config->expects($this->once())
$this->config->expects($this->any())
->method('getUserValue')
->with('admin', 'core', 'apporder')
->willReturn('[]');
->willReturnMap([
['admin', 'core', 'apporder', '[]', '[]'],
['admin', 'theming', 'background_image', BackgroundService::BACKGROUND_DEFAULT],
]);

$this->appManager->expects($this->once())
->method('getDefaultAppForUser')
->willReturn('forcedapp');

$this->initialStateService->expects($this->exactly(4))
$this->initialStateService->expects($this->exactly(7))
->method('provideInitialState')
->withConsecutive(
['shippedBackgrounds', BackgroundService::SHIPPED_BACKGROUNDS],
['themingDefaults'],
['userBackgroundImage'],
['themes', $themesState],
['enforceTheme', $enforcedTheme],
['isUserThemingDisabled', false],

+ 11
- 63
apps/theming/tests/ThemingDefaultsTest.php Ver fichero

@@ -455,82 +455,38 @@ class ThemingDefaultsTest extends TestCase {
'with fallback default' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '',
'userBackgroundColor' => '',
'userPrimaryColor' => '',
'expected' => BackgroundService::DEFAULT_COLOR,
],
'with custom admin background' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '#123',
'userBackgroundColor' => '',
'userPrimaryColor' => '',
'expected' => '#123',
],
'with custom invalid admin background' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => 'invalid-name',
'userBackgroundColor' => '',
'userPrimaryColor' => '',
'expected' => BackgroundService::DEFAULT_COLOR,
],
'with custom admin primary' => [
'disableTheming' => 'no',
'primaryColor' => '#aaa',
'backgroundColor' => '',
'userBackgroundColor' => '',
'userPrimaryColor' => '',
'expected' => '#aaa',
],
'with custom invalid admin primary' => [
'disableTheming' => 'no',
'primaryColor' => 'invalid',
'backgroundColor' => '',
'userBackgroundColor' => '',
'userPrimaryColor' => '',
'expected' => BackgroundService::DEFAULT_COLOR,
],
'with custom user background' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '',
'userBackgroundColor' => '#456',
'userPrimaryColor' => '#456',
'expected' => '#456',
],
'with custom invalid user primary' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '',
'userBackgroundColor' => '',
'userPrimaryColor' => 'invalid-name',
'expected' => BackgroundService::DEFAULT_COLOR,
],
'with custom user primary' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '',
'userBackgroundColor' => '',
'userPrimaryColor' => '#bbb',
'expected' => '#bbb',
],
'with custom invalid user background' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '',
'userBackgroundColor' => 'invalid-name',
'userPrimaryColor' => '',
'expected' => BackgroundService::DEFAULT_COLOR,
],
'with custom admin and user background' => [
'disableTheming' => 'no',
'primaryColor' => '',
'backgroundColor' => '#123',
'userBackgroundColor' => '#456',
'userPrimaryColor' => '#456',
'expected' => '#456',
'with disabled user theming primary' => [
'disableTheming' => 'yes',
'primaryColor' => '#aaa',
'userPrimaryColor' => '#bbb',
'expected' => '#aaa',
],
];
}
@@ -538,7 +494,7 @@ class ThemingDefaultsTest extends TestCase {
/**
* @dataProvider dataGetColorPrimary
*/
public function testGetColorPrimary(string $disableTheming, string $primaryColor, string $backgroundColor, string $userBackgroundColor, $userPrimaryColor, $expected) {
public function testGetColorPrimary(string $disableTheming, string $primaryColor, string $userPrimaryColor, string $expected) {
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->any())
->method('getUser')
@@ -552,15 +508,12 @@ class ThemingDefaultsTest extends TestCase {
->willReturnMap([
['theming', 'disable-user-theming', 'no', $disableTheming],
['theming', 'primary_color', '', $primaryColor],
['theming', 'background_color', '', $backgroundColor],
]);
$this->config
->expects($this->any())
->method('getUserValue')
->willReturnMap([
['user', 'theming', 'background_color', '', $userBackgroundColor],
['user', 'theming', 'primary_color', $userBackgroundColor, $userPrimaryColor],
]);
->with('user', 'theming', 'primary_color', '')
->willReturn($userPrimaryColor);

$this->assertEquals($expected, $this->template->getColorPrimary());
}
@@ -668,15 +621,10 @@ class ThemingDefaultsTest extends TestCase {
->method('deleteAppValue')
->with('theming', 'primary_color');
$this->config
->expects($this->exactly(2))
->expects($this->once())
->method('getAppValue')
->withConsecutive(
['theming', 'cachebuster', '0'],
['theming', 'primary_color', null],
)->willReturnOnConsecutiveCalls(
'15',
$this->defaults->getColorPrimary(),
);
->with('theming', 'cachebuster', '0')
->willReturn('15');
$this->config
->expects($this->once())
->method('setAppValue')

+ 236
- 100
cypress/e2e/theming/admin-settings.cy.ts Ver fichero

@@ -21,9 +21,15 @@
*/
/* 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'

const admin = new User('admin', 'admin')

@@ -36,15 +42,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 +74,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 +131,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 +173,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 +184,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 +226,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 +242,8 @@ describe('Remove the default background with a custom primary color', function()
})

describe('Remove the default background with a bright color', function() {
let selectedColor = ''

before(function() {
// Just in case previous test failed
cy.resetAdminTheming()
@@ -191,37 +253,51 @@ 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(() =>
cy.window().then((win) => {
const firstEntry = win.document.querySelector(
'.app-menu-main li img',
)
if (!firstEntry) {
return false
}
return getComputedStyle(firstEntry).filter === 'invert(1)'
}),
)
})
})

@@ -238,7 +314,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 +324,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 +407,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 +443,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 +464,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 +534,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 +561,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 +599,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))
})

+ 48
- 22
cypress/e2e/theming/themingUtils.ts Ver fichero

@@ -21,29 +21,54 @@
*/
import { colord } from 'colord'

const defaultNextcloudBlue = '#0082c9'
export const defaultPrimary = '#00679e'
export const defaultBackground = 'kamil-porembinski-clouds.jpg'

/**
* 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 && validateCSSVariable('--color-primary', expectedColor)
}

return isValidBackgroundColor && isValidBackgroundImage
/**
* 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 +83,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 +112,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()

+ 38
- 29
cypress/e2e/theming/user-background.cy.ts Ver fichero

@@ -80,7 +80,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 +95,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,14 +129,12 @@ 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'))
})
})

@@ -154,10 +152,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 +167,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
@@ -194,7 +194,7 @@ 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() {
@@ -240,15 +240,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 +278,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'))
})
})

+ 11
- 5
lib/private/Server.php Ver fichero

@@ -1185,14 +1185,20 @@ class Server extends ServerContainer implements IServerContainer {
}

if ($classExists && $c->get(\OCP\IConfig::class)->getSystemValueBool('installed', false) && $c->get(IAppManager::class)->isInstalled('theming') && $c->get(TrustedDomainHelper::class)->isTrustedDomain($c->getRequest()->getInsecureServerHost())) {
$backgroundService = new BackgroundService(
$c->get(IRootFolder::class),
$c->getAppDataDir('theming'),
$c->get(\OCP\IConfig::class),
$c->get(ISession::class)->get('user_id'),
);
$imageManager = new ImageManager(
$c->get(\OCP\IConfig::class),
$c->getAppDataDir('theming'),
$c->get(IURLGenerator::class),
$this->get(ICacheFactory::class),
$this->get(LoggerInterface::class),
$this->get(ITempManager::class),
$this->get(BackgroundService::class),
$c->get(ICacheFactory::class),
$c->get(LoggerInterface::class),
$c->get(ITempManager::class),
$backgroundService,
);
return new ThemingDefaults(
$c->get(\OCP\IConfig::class),
@@ -1204,7 +1210,7 @@ class Server extends ServerContainer implements IServerContainer {
$imageManager,
$c->get(IAppManager::class),
$c->get(INavigationManager::class),
$c->get(BackgroundService::class),
$backgroundService,
);
}
return new \OC_Defaults();

+ 2
- 2
lib/private/legacy/OC_Defaults.php Ver fichero

@@ -71,8 +71,8 @@ class OC_Defaults {
$this->defaultFDroidClientUrl = $config->getSystemValue('customclient_fdroid', 'https://f-droid.org/packages/com.nextcloud.client/');
$this->defaultDocBaseUrl = 'https://docs.nextcloud.com';
$this->defaultDocVersion = \OC_Util::getVersion()[0]; // used to generate doc links
$this->defaultColorBackground = '#0069c3';
$this->defaultColorPrimary = '#0082c9';
$this->defaultColorBackground = '#00679e';
$this->defaultColorPrimary = '#00679e';
$this->defaultTextColorPrimary = '#ffffff';
$this->defaultProductName = 'Nextcloud';


Cargando…
Cancelar
Guardar