Tester un menu de navigation dans Wagtail avec Factory Boy

Un menu de navigation dépend du contenu d'un site. Dans ce tutoriel on crée le contenu d'un site Wagtail avec Factory Boy et avec ça teste le menu de navigation.

14 Juillet 2020 20:43
Thèmes: Navigation Tester

Dans un didacticiel précédent, on a créé un menu de navigation qui peut gérer les pages Wagtail ainsi que les pages en dehors de l'arbre Wagtail, est multilingue et modifiable via admin. Dans un autre tutoriel, on a mis en place une usine (factory) pour tester les pages Wagtail. On va réunir les deux pour tester le menu de navigation. Puisqu'on a déjà mis en place une usine de pages, on peut créer du contenu pour le site. Configurons du contenu pour nos tests: un site, une page d'accueil, une page d'index d'article et deux pages d'article. Notez que on définit les champs show_in_menus sur True. Dans notre répertoire /tests, créez un fichier test_menus.py:

from .factories import HomePageFactory, ArticlePageFactory, ArticleIndexPageFactory
from django.test import TestCase
from wagtail.core.models import Page, Site

class TestMenus(TestCase):

    @classmethod
    def setUpTestData(cls):
        cls.site = Site.objects.create(is_default_site=True, root_page=Page.get_first_root_node())
        cls.homepage = HomePageFactory()
        cls.articleindexpage = ArticleIndexPageFactory(parent=cls.homepage)
        cls.articlepage1 = ArticlePageFactory(parent=cls.articleindexpage, show_in_menus=True)
        cls.articlepage2 = ArticlePageFactory(parent=cls.articleindexpage, show_in_menus=True)

Avant de pouvoir créer un menu et des éléments de menu, on doit créer une usine pour eux, dans notre fichier /tests/factories.py. L'usine du menu est simple:

from ..models import Menu

class MenuFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Menu

    title = factory.Sequence(lambda n: 'Menu {0}'.format(n))

On a déjà expliqué l'utilisation de Sequence pour générer automatiquement un titre. L'usine des éléments de menu peut être définie comme suit:

from ..models import MenuItem

class MenuItemFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = MenuItem

    menu = factory.SubFactory(MenuFactory)

La relation de clé étrangère (foreign key) entre le menu et l'élément de menu est établie via une SubFactory. Dans la configuration de test dans le fichier test_menus.py, on crée un menu et trois éléments de menu avec quelques champs définis sur des valeurs différentes:

cls.menu = MenuFactory()
# for the menu-items we need to establish a sort order (class Orderable)
cls.menuitem_ordinary = MenuItemFactory(menu=cls.menu, sort_order=1, title="Ordinary",
                                        link_url='/ordinary/', show_when='always')
cls.menuitem_guest = MenuItemFactory(menu=cls.menu, sort_order=2, title="Guest", link_url='/guest/',
                                     show_when='not_logged_in')
cls.menuitem_articlepage1 = MenuItemFactory(menu=cls.menu, sort_order=3, link_page=cls.articlepage1)

Comme le modèle MenuItem est sous-classé de la classe Orderable, on ajoute manuellement un sort_order. De plus, on a défini un title, un link_url et différentes valeurs pour le champ show_when, à utiliser dans nos tests. Il est temps d'écrire notre premier test: vérifiez si notre fonction get_menu génère correctement un menu à partir d'une page qui a des enfants:

def test_get_menu_of_page(self):
    menu = get_menu(slug=None, page=self.articleindexpage, logged_in=True)
    self.assertEqual(menu[0]['title'], self.articlepage1.title)
    self.assertEqual(menu[0]['url'], self.articlepage1.url)
    self.assertEqual(menu[0]['page'].title, self.articlepage1.title)
    self.assertEqual(len(menu), 2)

On peut exécuter le test avec:

python3 manage.py test cms.tests.test_menus

Jusqu'ici tout va bien. Maintenant, testons un menu fait à la main:

from django.utils import translation

def test_get_handmade_menu(self):
    menu = get_menu(self.menu.slug, None, True)
    self.assertEqual(menu[0]['title'], 'Ordinary')
    # the expected url is in the current language
    expected_url = '/' + translation.get_language() + '/ordinary/'
    self.assertEqual(menu[0]['url'], expected_url)

La fonction get_menu génère le menu à partir du slug de l'instance de menu, qui est automatiquement généré à partir du titre dans notre modèle. Le premier élément de menu doit alors avoir le titre du premier élément de menu. Puisque notre site est multilingue, on s'attend à ce que l'url soit préfixée avec le code de langue actuel, on utilise donc la fonction get_language() pour vérifier cela. Tester si la fonction get_menu récupère le paramètre logged_in est simple:

def test_get_menu_logged_in_or_not(self):
    menu = get_menu(self.menu.slug, None, True)
    # menu should only have two items
    self.assertEqual(len(menu), 2)
    menu = get_menu(self.menu.slug, None, False)
    self.assertEqual(len(menu), 3)

On veut ajouter quelques tests supplémentaires sur la façon dont get_menu gère les langues. Pour cela, on introduit un code de langue étrangère et une page dans une langue étrangère dans notre configuration:

from django.conf import settings
from wagtailtrans.models import Language, TranslatablePage

cls.foreign_language_code = [code for code, lang in settings.LANGUAGES if code != settings.LANGUAGE_CODE][0]
# note: Wagtailtrans automatically creates a language tree for every language that is defined
cls.foreign_language = Language.objects.get_or_create(code=cls.foreign_language_code)[0]
cls.foreign_articlepage1 = TranslatablePage.objects.get(language=cls.foreign_language, canonical_page=cls.articlepage1)

On récupère le code de langue étrangère dans nos paramètres. Wagtailtrans possède un modèle de Language spécial, dont on a besoin d'une instance. Lorsqu'une page est créée (dans notre cas articlepage1), Wagtailtrans génère automatiquement une page pour chaque langue dans les paramètres. Par conséquent, on peut récupérer une version en langue étrangère de cette page en utilisant les champs language et canonical_page. On peut maintenant tester la méthode trans_page() et trans_url() du modèle MenuItem:

def test_menuitem_trans_page_for_foreign_language(self):
self.assertEqual(self.menuitem_articlepage1.trans_page(self.foreign_language_code).url, self.foreign_articlepage1.url)

def test_menuitem_trans_page_for_canonical_language(self):
self.assertEqual(self.menuitem_articlepage1.trans_page(settings.LANGUAGE_CODE).url, self.articlepage1.url)

def test_menuitem_trans_url_method(self):
self.assertEqual(self.menuitem_articlepage1.trans_url(self.foreign_language_code), self.foreign_articlepage1.url)

Exécutez coverage pour vérifier la quantité de code qu'on a couverte jusqu'à présent:

coverage run --source=cms manage.py test cms
coverage html

Nos balises de modèle sont suffisamment bien couvertes. Il est temps de passer au test de la langue dans views.py.

Commentez cet article (connectez-vous d'abord ou confirmez par nom et email ci-dessous)