[IMP] test_themes, theme_test_custo: add new test theme for tests

This commit introduces a new theme for tests purpose.
The goal is to be able to create records and simulate a real use case
without only testing through unit tests in python.
For instance, with this commit, we now fully test:
- menu creation
- page creation
- new header & footer template
- new image shape & background shape

This will be extended later to be able to test other stuff too, maybe
like changing the website logo (not supported yet). Also, our existing
standalone test in themes should use this theme instead of nano to not
rely on a real theme that might change.

Courtesy of NMI as this theme is partly based on his job.

closes odoo/design-themes#586

Related: odoo/odoo#99099
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Romain Derie
2022-08-29 21:55:51 +00:00
parent ca449f01f8
commit 0e2497b8e8
23 changed files with 530 additions and 6 deletions
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+24
View File
@@ -0,0 +1,24 @@
{
'name': 'Custom Theme (Testing suite)',
'summary': '',
'description': '',
# Remove the `/Hidden` part to make it selectable for tests purpose
'category': 'Theme/Hidden',
'version': '1.0',
'depends': ['website'],
'data': [
'data/images.xml',
'data/ir_asset.xml',
'data/menu.xml',
'data/pages.xml',
'data/shapes.xml',
'views/footer.xml',
'views/header.xml',
],
'assets': {
'web.assets_tests': [
'theme_test_custo/static/tests/tours/**/*',
],
},
'license': 'LGPL-3',
}
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="logo" model="theme.ir.attachment">
<field name="name">Test Theme Custom Logo</field>
<field name="url">/theme_test_custo/static/src/img/logo.jpg</field>
<field name="key">theme_test_custo.logo</field>
</record>
</odoo>
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="theme_test_custo.bootstrap_overridden_scss" model="theme.ir.asset">
<field name="key">theme_test_custo.bootstrap_overridden_scss</field>
<field name="name">Bootstrap overridden SCSS</field>
<field name="bundle">web._assets_frontend_helpers</field>
<field name="directive">prepend</field>
<field name="path">theme_test_custo/static/src/scss/bootstrap_overridden.scss</field>
</record>
<record id="theme_test_custo.primary_variables_scss" model="theme.ir.asset">
<field name="key">theme_test_custo.primary_variables_scss</field>
<field name="name">Primary variables SCSS</field>
<field name="bundle">web._assets_primary_variables</field>
<field name="path">theme_test_custo/static/src/scss/primary_variables.scss</field>
</record>
</odoo>
+80
View File
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Case 1: Create a new navbar menu -->
<record id="menu_example_1" model="theme.website.menu">
<field name="name">Example 1</field>
<field name="sequence" type="int">20</field>
</record>
<!-- Case 2: Create child menu below a theme.website.menu -->
<record id="menu_example_1_item_1" model="theme.website.menu">
<field name="name">Item 1</field>
<field name="url">/example</field>
<field name="parent_id" ref="menu_example_1"/>
<field name="sequence" type="int">21</field>
</record>
<record id="menu_example_1_item_2" model="theme.website.menu">
<field name="name">Item 2</field>
<field name="url">/example/item-2</field>
<field name="parent_id" ref="menu_example_1"/>
<field name="sequence" type="int">22</field>
</record>
<record id="menu_example_1_item_3" model="theme.website.menu">
<field name="name">Item 3</field>
<field name="url">/example/item-3</field>
<field name="parent_id" ref="menu_example_1"/>
<field name="sequence" type="int">23</field>
</record>
<!-- Case 3: Create a mega menu -->
<record id="menu_products" model="theme.website.menu">
<field name="name">Mega Menu</field>
<field name="url">/mega-menu</field>
<field name="sequence" type="int">30</field>
<field name="mega_menu_content" type="html">
<section class="o_cc o_cc2 pt32 pb32">
<div class="container">
<div class="row">
<div class="col-lg-3">
<i class="fa fa-cube"/>
<p>Text</p>
</div>
<div class="col-lg-3">
<i class="fa fa-cube"/>
<p>Text</p>
</div>
<div class="col-lg-3">
<i class="fa fa-cube"/>
<p>Text</p>
</div>
<div class="col-lg-3">
<i class="fa fa-cube"/>
<p>Text</p>
</div>
</div>
</div>
</section>
</field>
</record>
<!-- Case 4: Create a new whole menu hierarchy (to be put elsewhere) -->
<record id="main_menu_animals" model="theme.website.menu">
<field name="name">Main Menu Animals</field>
<field name="use_main_menu_as_parent" eval="False"/>
<field name="url">/main-menu-animals</field>
</record>
<record id="menu_dogs" model="theme.website.menu">
<field name="name">Dogs</field>
<field name="url">/dogs</field>
<field name="parent_id" ref="main_menu_animals"/>
<field name="sequence" type="int">10</field>
</record>
<record id="menu_cats" model="theme.website.menu">
<field name="name">Cats</field>
<field name="url">/cats</field>
<field name="parent_id" ref="main_menu_animals"/>
<field name="sequence" type="int">20</field>
</record>
</odoo>
+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="view_example" name="Example">
<t t-set="additional_title" t-valuef="Example"/>
<t t-call="website.layout">
<div id="wrap" class="oe_structure">
<!-- Image Shape -->
<section class="s_text_image pt80 pb80">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6 pt16 pb16">
<h2>A Section Subtitle</h2>
<p>Write one or two paragraphs describing your product or services. To be successful your content needs to be useful to your readers.</p>
<p>Start with the customer - find out what they want and give it to them.</p>
<p><a href="#" class="btn btn-primary mb-2">Learn more</a></p>
</div>
<div class="col-lg-6 pt16 pb16">
<img src="/web_editor/image_shape/website.s_text_image_default_image/theme_test_custo/blob/01.svg" class="img img-fluid mx-auto" alt="" data-original-mimetype="image/jpeg" data-file-name="01.svg" data-shape="theme_test_custo/blob/01" data-shape-colors=";;;;"/>
</div>
</div>
</div>
</section>
<!-- Background Shape -->
<section class="s_text_block pt80 pb200" data-name="Text" data-oe-shape-data="{'shape': 'theme_test_custo/curves/01','flip':[]}">
<div class="o_we_shape o_theme_test_custo_curves_01"/>
<div class="s_allow_columns container">
<p class="o_default_snippet_text">Great stories have a <b class="o_default_snippet_text">personality</b>. Consider telling a great story that provides personality. Writing a story with personality for potential clients will assist with making a relationship connection. This shows up in small quirks like word choices or phrases. Write from your point of view, not from someone else's experience.</p>
</div>
</section>
</div>
</t>
</template>
<record id="page_example" model="theme.website.page">
<field name="view_id" ref="view_example"/>
<field name="is_published" eval="True"/>
<field name="url">/example</field>
</record>
</odoo>
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Background Shape -->
<template id="snippet_options_background_options" inherit_id="web_editor.snippet_options_background_options" name="Background Shape Custom Option">
<xpath expr="//we-select-pager[hasclass('o_we_shape_menu')]/we-select-page[last()]" position="after">
<we-select-page string="Custom Shapes">
<we-button data-shape="theme_test_custo/hexagons/01" data-select-label="Hexagon 01"/>
<we-button data-shape="theme_test_custo/curves/01" data-select-label="Curve 01"/>
</we-select-page>
</xpath>
</template>
<record id="shape_hexagon_01" model="theme.ir.attachment">
<field name="name">Hexagon Shape 01</field>
<field name="key">theme_test_custo.shape_hexagon_01</field>
<field name="url">/web_editor/shape/illustration/hexagons/01.svg</field>
</record>
<record id="shape_curve_01" model="theme.ir.attachment">
<field name="name">Curve Shape 01</field>
<field name="key">theme_test_custo.shape_curve_01</field>
<field name="url">/web_editor/shape/illustration/curves/01.svg</field>
</record>
<!-- Image Shape -->
<template id="snippet_options" inherit_id="web_editor.snippet_options" name="Image Shape Custom Option">
<xpath expr="//we-select-pager[@data-name='shape_img_opt']/we-select-page[last()]" position="after">
<we-select-page string="Custom Shapes">
<we-button data-set-img-shape="theme_test_custo/blob/01" data-select-label="Blob 01"/>
</we-select-page>
</xpath>
</template>
<record id="img_shape_blob_01" model="theme.ir.attachment">
<field name="name">Blob Shape 01</field>
<field name="key">theme_test_custo.img_shape_blob_01</field>
<field name="url">/web_editor/image_shape/blob/01.svg</field>
</record>
</odoo>
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import theme_models
+20
View File
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class TestThemeCustom(models.AbstractModel):
_inherit = 'theme.utils'
@property
def _header_templates(self):
return ['theme_test_custo.template_header_custom'] + super()._header_templates
@property
def _footer_templates(self):
return ['theme_test_custo.template_footer_custom'] + super()._footer_templates
def _theme_test_custo_post_copy(self, mod):
self.enable_view('theme_test_custo.template_header_custom')
self.enable_view('theme_test_custo.template_footer_custom')
@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="600">
<defs>
<clipPath id="clip-path" clipPathUnits="objectBoundingBox">
<use xlink:href="#filterPath" fill="none"></use>
</clipPath>
<path id="filterPath" d="M0.7988,0.0922c0.0892,0.0552,0.1639,0.1385,0.1901,0.2339c0.0262,0.0946,0.003,0.2017-0.0385,0.2948c-0.0413,0.0941-0.1015,0.1737-0.1791,0.2419c-0.0776,0.0682-0.1727,0.1256-0.2749,0.1356c-0.103,0.01-0.2125-0.0265-0.3046-0.0904s-0.1654-0.155-0.1856-0.2541c-0.0204-0.0991,0.0131-0.2059,0.0478-0.3135c0.0348-0.1069,0.0718-0.2139,0.1465-0.2734C0.2761,0.0076,0.3899-0.0054,0.4987,0.0018C0.6067,0.0088,0.7097,0.037,0.7988,0.0922z"/>
</defs>
<svg viewBox="0 0 1 1" id="preview" preserveAspectRatio="none">
<use xlink:href="#filterPath" fill="darkgrey"></use>
</svg>
<image xlink:href="" clip-path="url(#clip-path)"></image>
</svg>

After

Width:  |  Height:  |  Size: 961 B

@@ -0,0 +1,4 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1920" height="1080">
<path d="M1884.9,924.9c-21.7,21.7-51.7,35.1-84.9,35.1h-585H705H120c-33.1,0-63.1,13.4-84.9,35.1C13.4,1016.9,0,1046.9,0,1080h120
h585h510h705V840C1920,873.1,1906.6,903.1,1884.9,924.9z" style="fill: #7C6576;"/>
</svg>

After

Width:  |  Height:  |  Size: 305 B

@@ -0,0 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="86" height="100">
<polygon points="0 25, 43 0, 86 25, 86 75, 43 100, 0 75" style="fill: #3AADAA;"/>
</svg>

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="234" height="60" viewBox="0 0 234 60">
<rect x="0" y="0" width="234" height="60" fill="#FFFFFF"/>
<path fill="#714B67" d="M141,17.32v27c0,2.49-2.01,4.5-4.5,4.5h-39c-2.49,0-4.5-2.01-4.5-4.5v-27c0-2.49,2.01-4.5,4.5-4.5h39
C138.99,12.82,141,14.83,141,17.32z M136.5,43.76V17.88c0-0.31-0.25-0.56-0.56-0.56H98.06c-0.31,0-0.56,0.25-0.56,0.56v25.88
c0,0.31,0.25,0.56,0.56,0.56h37.88C136.25,44.32,136.5,44.07,136.5,43.76z M108.75,24.82c0,2.07-1.68,3.75-3.75,3.75
s-3.75-1.68-3.75-3.75s1.68-3.75,3.75-3.75S108.75,22.75,108.75,24.82z M102,35.32l3.7-3.7c0.44-0.44,1.15-0.44,1.59,0l3.7,3.7
l11.2-11.2c0.44-0.44,1.15-0.44,1.59,0l8.2,8.2v7.5h-30V35.32z"/>
</svg>

After

Width:  |  Height:  |  Size: 780 B

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="234" height="60" viewBox="0 0 234 60">
<rect x="0" y="0" width="234" height="60" fill="#FFFFFF"/>
<path fill="#714B67" d="M141,17.32v27c0,2.49-2.01,4.5-4.5,4.5h-39c-2.49,0-4.5-2.01-4.5-4.5v-27c0-2.49,2.01-4.5,4.5-4.5h39
C138.99,12.82,141,14.83,141,17.32z M136.5,43.76V17.88c0-0.31-0.25-0.56-0.56-0.56H98.06c-0.31,0-0.56,0.25-0.56,0.56v25.88
c0,0.31,0.25,0.56,0.56,0.56h37.88C136.25,44.32,136.5,44.07,136.5,43.76z M108.75,24.82c0,2.07-1.68,3.75-3.75,3.75
s-3.75-1.68-3.75-3.75s1.68-3.75,3.75-3.75S108.75,22.75,108.75,24.82z M102,35.32l3.7-3.7c0.44-0.44,1.15-0.44,1.59,0l3.7,3.7
l11.2-11.2c0.44-0.44,1.15-0.44,1.59,0l8.2,8.2v7.5h-30V35.32z"/>
</svg>

After

Width:  |  Height:  |  Size: 780 B

@@ -0,0 +1,3 @@
// Cards
$card-border-width: 0 !default;
@@ -0,0 +1,73 @@
// -------------------------------------------------------------------------- //
// PRESETS
// -------------------------------------------------------------------------- //
$o-website-values-palettes: (
(
// Colors
'color-palettes-name': 'custom-theme',
// Fonts
'font': 'Source Sans Pro',
'headings-font': 'Poppins',
'navbar-font': 'Source Sans Pro',
'buttons-font': 'Source Sans Pro',
// Header
'header-template': 'custom-theme',
'logo-height': 4rem,
// Footer
'footer-template': 'custom-theme',
),
);
// -------------------------------------------------------------------------- //
// FONTS
// -------------------------------------------------------------------------- //
$o-theme-font-configs: (
'Source Sans Pro': (
'family': ('Source Sans Pro', sans-serif),
'url': 'Source+Sans+Pro:300,400,400i,600',
),
'Poppins': (
'family': ('Poppins', sans-serif),
'url': 'Poppins:400,500',
),
);
// -------------------------------------------------------------------------- //
// COLORS
// -------------------------------------------------------------------------- //
$o-color-palettes: map-merge($o-color-palettes,
(
'custom-theme': (
'o-color-1': #BEDB39,
'o-color-2': #2C3E50,
'o-color-3': #ECF0F1,
'o-color-4': #FFFFFF,
'o-color-5': #212529,
'menu': 1,
'footer': 3,
'copyright': 5,
),
)
);
$o-selected-color-palettes-names: append($o-selected-color-palettes-names, 'custom-theme');
// ------------------------------------------------------------------------------ //
// SHAPES
// ------------------------------------------------------------------------------ //
$o-bg-shapes: map-merge($o-bg-shapes,
(
'theme_test_custo':(
'hexagons/01': ('position': center center, 'size': auto 100%, 'colors': (1), 'repeat-y': false),
'curves/01': ('position': center bottom, 'size': 100% auto, 'colors': (2), 'repeat-y': false),
),
)
);
@@ -0,0 +1,64 @@
/** @odoo-module */
import wTourUtils from 'website.tour_utils';
wTourUtils.registerEditionTour('theme_menu_hierarchies', {
url: '/example',
test: true,
}, [
{
content: 'Check Mega Menu is correctly created',
trigger: 'iframe #top_menu a.o_mega_menu_toggle',
}, {
content: 'Check Mega Menu content',
trigger: 'iframe #top_menu div.o_mega_menu.show .fa-cube',
run: () => null, // It's a check.
}, {
content: 'Check new top level menu is correctly created',
trigger: 'iframe #top_menu .nav-item.dropdown .dropdown-toggle:contains("Example 1")',
}, {
content: 'Check sub menu are correctly created',
trigger: 'iframe #top_menu .dropdown-menu.show a.dropdown-item:contains("Item 1")',
run: () => null, // It's a check.
}, {
content: 'The new menu hierarchy should not be included in the navbar',
trigger: 'iframe body:not(:has(#top_menu a[href="/dogs"]))',
run: () => null, // It's a check.
}, {
content: 'The new menu hierarchy should be inside the footer',
trigger: 'iframe footer ul li a[href="/dogs"]',
run: () => null, // It's a check.
},
wTourUtils.clickOnEdit(),
{
content: 'Click on footer',
// TODO: this extra_trigger should be part of the `clickOnEdit` util
extra_trigger: "#oe_snippets.o_loaded",
trigger: 'iframe footer',
}, {
content: 'The theme custom footer template should be listed and selected',
trigger: 'we-select[data-variable="footer-template"] we-toggler img[src*="/theme_test_custo"]',
run: () => null, // It's a check.
}, {
content: 'Click on header',
trigger: 'iframe header',
}, {
content: 'The theme custom header template should be listed and selected',
trigger: 'we-select[data-variable="header-template"] we-toggler img[src*="/theme_test_custo"]',
run: () => null, // It's a check.
}, {
content: 'Click on image which has a shape',
trigger: 'iframe #wrap .s_text_image img[data-shape]',
}, {
content: 'The theme custom "Blob 01" shape should be listed and selected',
trigger: 'we-select[data-name="shape_img_opt"] we-toggler:contains("Blob 01")',
run: () => null, // It's a check.
}, {
content: 'Click on section which has a bg shape',
trigger: 'iframe #wrap .s_text_block[data-oe-shape-data]',
}, {
content: 'The theme custom "Curve 01" shape should be listed and selected',
trigger: 'we-select[data-name="bg_shape_opt"] we-toggler:contains("Curve 01")',
run: () => null, // It's a check.
},
]);
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_theme_creation
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import HttpCase, tagged
# TODO: `test_themes` tag should not be there, runbot config should be adapted
# to test this module too. There is a special config for the theme repo.
@tagged('post_install', '-at_install', 'test_themes')
class Crawler(HttpCase):
def test_01_menu_hierarchies(self):
theme_custo = self.env.ref('base.module_theme_test_custo')
website = self.env['website'].browse(1)
website.theme_id = theme_custo.id
theme_custo.with_context(load_all_views=True, apply_new_theme=True)._theme_load(website)
self.start_tour('/@/example', "theme_menu_hierarchies", login='admin')
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Theme Footer - Option -->
<template id="template_footer_opt" inherit_id="website.snippet_options" name="Test Theme Custom Footer Template - Option">
<xpath expr="//we-select[@data-variable='footer-template']" position="inside">
<we-button title="Custom Theme"
data-customize-website-views="theme_test_custo.template_footer_custom"
data-customize-website-variable="'custom-theme'"
data-img="/theme_test_custo/static/src/img/template_footer_opt.svg"/>
</xpath>
</template>
<!-- Theme Footer - Layout -->
<template id="template_footer_custom" inherit_id="website.layout" name="Test Theme Custom Footer Template">
<xpath expr="//div[@id='footer']" position="replace">
<div id="footer" class="oe_structure oe_structure_solo" t-ignore="true" t-if="not no_footer">
<section class="s_text_block pt24 pb24" data-snippet="s_text_block" data-name="Text">
<div class="container-fluid px-lg-5">
<div class="row">
<div class="col-lg-1 offset-lg-2 pt24 pb24">
<img src="/web/image/theme_test_custo.logo" class="img-fluid rounded-circle" alt=""/>
</div>
<div class="col-lg-6 pt24 pb24 text-center">
<h5 class="mb-4">Useful Links</h5>
<ul class="list-unstyled">
<li t-foreach="request.env['website.menu'].search([('url', '=', '/main-menu-animals'), ('website_id', '=', request.website.id)]).child_id" t-as="menu">
<a t-att-href="menu.url" t-esc="menu.name"/>
</li>
</ul>
</div>
<div class="col-lg-1 pt24 pb24">
<img src="/web/image/theme_test_custo.logo" class="img-fluid rounded-circle" alt=""/>
</div>
</div>
</div>
</section>
</div>
</xpath>
</template>
</odoo>
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Theme Header - Option -->
<template id="template_header_opt" inherit_id="website.snippet_options" name="Test Theme Custom Header Template - Option">
<xpath expr="//we-select[@data-variable='header-template']" position="inside">
<we-button title="Custom Theme"
data-customize-website-views="theme_test_custo.template_header_custom"
data-customize-website-variable="'custom-theme'"
data-img="/theme_test_custo/static/src/img/template_header_opt.svg"/>
</xpath>
</template>
<!-- Theme Header - Layout -->
<template id="template_header_custom" inherit_id="website.layout" name="Test Theme Custom Header Template">
<xpath expr="//header//nav" position="replace">
<nav data-name="Navbar" class="navbar navbar-expand-lg">
<div class="container">
<t t-call="website.placeholder_header_brand"/>
<t t-call="website.navbar_nav">
<!-- Menu -->
<t t-foreach="website.menu_id.child_id" t-as="submenu">
<t t-call="website.submenu">
<t t-set="item_class" t-valuef="nav-item"/>
<t t-set="link_class" t-valuef="nav-link"/>
</t>
</t>
<!-- Sign In -->
<t t-call="portal.placeholder_user_sign_in">
<t t-set="_item_class" t-valuef="nav-item"/>
<t t-set="_link_class" t-valuef="nav-link text-900"/>
</t>
<!-- User Dropdown -->
<t t-call="portal.user_dropdown">
<t t-set="_user_name" t-value="true"/>
<t t-set="_item_class" t-valuef="nav-item dropdown ml-auto"/>
<t t-set="_link_class" t-valuef="nav-link text-900"/>
</t>
</t>
</div>
</nav>
</xpath>
</template>
</odoo>