diff --git a/jest.config.js b/jest.config.js index afddd7fd016117bd1da036cf866227645e66e512..676221f6626e4553dd4bc7479f7d718d8e6b1192 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,6 +18,13 @@ module.exports = { globals: { __ALLOW_HTTP__: false, __TARGET__: 'browser', - cozy: {}, - }, + cozy: { + bar: { + BarLeft: ({ children }) => children, + BarRight: ({ children }) => children, + BarCenter: ({ children }) => children, + setTheme: () => null, + }, + }, + } } diff --git a/src/components/Header/CozyBar.spec.tsx b/src/components/Header/CozyBar.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..35489f78c8e7f4deaaa5ab0ad9eab2371049d605 --- /dev/null +++ b/src/components/Header/CozyBar.spec.tsx @@ -0,0 +1,57 @@ +'use strict' +/* eslint-env jest */ +import React from 'react' +import { shallow } from 'enzyme' +import CozyBar from 'components/Header/CozyBar' +import { ScreenType } from 'enum/screen.enum' +import * as MockRecoil from '../../services/__mocks__/recoil' +import * as Recoil from 'recoil' +import { ModalState } from 'models' +import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +jest.mock('recoil') +const { + useRecoilValue, + useSetRecoilState, +} = (Recoil as unknown) as typeof MockRecoil + +describe('CozyBar component', () => { + it('should be rendered correctly', () => { + useRecoilValue.mockReturnValueOnce(ScreenType.MOBILE) + const component = shallow(<CozyBar />).getElement() + expect(component).toMatchSnapshot() + }) + + it('should call handleClickFeedbacks', () => { + useRecoilValue.mockReturnValueOnce(ScreenType.MOBILE) + useSetRecoilState.mockReturnValueOnce((t: ModalState) => t) + const component = shallow(<CozyBar />) + component.find(StyledIconButton).simulate('click') + expect(useSetRecoilState).toBeCalled() + }) + + it('should call handleClickBack', () => { + useRecoilValue.mockReturnValueOnce(ScreenType.MOBILE) + const component = shallow(<CozyBar displayBackArrow={true} />) + component + .find(StyledIconButton) + .first() + .simulate('click') + expect(useSetRecoilState).toBeCalled() + }) + + it('should not be rendered with default value', () => { + const component = shallow(<CozyBar />).getElement() + expect(component).toBeNull() + }) +}) diff --git a/src/components/Header/__snapshots__/CozyBar.spec.tsx.snap b/src/components/Header/__snapshots__/CozyBar.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..d5a08f77ebbfef342373f5bd2fd469b2f802a04e --- /dev/null +++ b/src/components/Header/__snapshots__/CozyBar.spec.tsx.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CozyBar component should be rendered correctly 1`] = ` +<React.Fragment> + <BarCenter> + <div + className="cozy-bar" + > + <span + className="app-title" + > + COMMON.APP_TITLE + </span> + </div> + </BarCenter> + <BarRight> + <StyledIconButton + className="cv-button" + icon="test-file-stub" + onClick={[Function]} + size={40} + /> + </BarRight> +</React.Fragment> +`; diff --git a/src/components/LegalNotice/LegalNoticeLink.spec.tsx b/src/components/LegalNotice/LegalNoticeLink.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1e5c27d0cc5ec956b77a45e9d2d4fd73eb44b62f --- /dev/null +++ b/src/components/LegalNotice/LegalNoticeLink.spec.tsx @@ -0,0 +1,22 @@ +'use strict' +/* eslint-env jest */ +import React from 'react' +import { shallow } from 'enzyme' +import LegalNoticeLink from 'components/LegalNotice/LegalNoticeLink' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +describe('LegalNoticeView component', () => { + it('should be rendered correctly', () => { + const component = shallow(<LegalNoticeLink />).getElement() + expect(component).toMatchSnapshot() + }) +}) diff --git a/src/components/LegalNotice/LegalNoticeView.spec.tsx b/src/components/LegalNotice/LegalNoticeView.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3f8f4366d26392dad5ecb12d2645e523f5857520 --- /dev/null +++ b/src/components/LegalNotice/LegalNoticeView.spec.tsx @@ -0,0 +1,12 @@ +'use strict' +/* eslint-env jest */ +import React from 'react' +import { shallow } from 'enzyme' +import LegalNoticeView from 'components/LegalNotice/LegalNoticeView' + +describe('LegalNoticeView component', () => { + it('should be rendered correctly', () => { + const component = shallow(<LegalNoticeView />).getElement() + expect(component).toMatchSnapshot() + }) +}) diff --git a/src/components/LegalNotice/__snapshots__/LegalNoticeLink.spec.tsx.snap b/src/components/LegalNotice/__snapshots__/LegalNoticeLink.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..21fd8812daa636ed1024359edac706c777f38768 --- /dev/null +++ b/src/components/LegalNotice/__snapshots__/LegalNoticeLink.spec.tsx.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LegalNoticeView component should be rendered correctly 1`] = ` +<div + className="legal-notice-root" +> + <div + className="legal-notice-content" + > + <div + className="legal-notice-header text-14-normal-uppercase" + > + LEGAL.TITLE_LEGAL + </div> + <ForwardRef + className="legal-notice-card-link" + to="options/legalnotice" + > + <StyledCard> + <div + className="legal-notice-card" + > + <div + className="legal-notice-card-content" + > + <StyledIcon + className="legal-notice-card-content-icon" + icon="test-file-stub" + size={50} + /> + <div + className="legal-notice-card-content-title" + > + LEGAL.READ_LEGAL + </div> + </div> + </div> + </StyledCard> + </ForwardRef> + </div> +</div> +`; diff --git a/src/components/LegalNotice/__snapshots__/LegalNoticeView.spec.tsx.snap b/src/components/LegalNotice/__snapshots__/LegalNoticeView.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..51c6e47f756f24156872859c70bcdb8467bce670 --- /dev/null +++ b/src/components/LegalNotice/__snapshots__/LegalNoticeView.spec.tsx.snap @@ -0,0 +1,284 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LegalNoticeView component should be rendered correctly 1`] = ` +<React.Fragment> + <CozyBar + displayBackArrow={true} + titleKey="COMMON.APP_LEGAL_TITLE" + /> + <Header + desktopTitleKey="COMMON.APP_LEGAL_TITLE" + displayBackArrow={true} + setHeaderHeight={[Function]} + /> + <Content + height={0} + > + <div + className="legal-notice-root" + > + <div + className="legal-notice-content" + > + <h2> + Mentions légales de Ecolyo + </h2> + <p> + Site du service Ecolyo : + localhost + </p> + <p> + Métropole de Lyon - 20, rue du Lac – CS 33569 - 69505 Lyon cedex 03 + + </p> + <p> + Tél : (33) 4 78 63 40 40 + </p> + + <p + className="ln-contact" + > + contact(at)grandlyon.com + </p> + <div + className="text-16-normal" + > + <div + className="legal-notice-oneline" + > + <span + className="text-16-bold" + > + Directeur de publication + </span> + : Blandine MELAY + </div> + + <div + className="legal-notice-oneline" + > + <span + className="text-16-bold" + > + Animation éditoriale, gestion et mise à jour + </span> + + : Marion BERTHOLON, Maria Inés LEAL + </div> + + <div + className="legal-notice-oneline" + > + <span + className="text-16-bold" + > + Photographies + </span> + : sauf mention contraire, les photos sont la propriété de la Métropole de Lyon + </div> + + <div + className="legal-notice-oneline" + > + <span + className="text-16-bold" + > + Conception et Charte graphique + </span> + + : Florent Dufier - + + <a + href="https://florentdufier.myportfolio.com/" + rel="noopener noreferrer" + target="_blank" + > + https://florentdufier.myportfolio.com/ + </a> + </div> + <div + className="legal-notice-oneline" + > + <span + className="text-16-bold" + > + Réalisation technique + </span> + : Métropole de Lyon, Sopra Steria, Cozy Cloud, Clever Age + </div> + <div + className="legal-notice-oneline" + > + <span + className="text-16-bold" + > + Maintenance technique + </span> + : Délégation Développement économique, emploi & savoirs - Innovation numérique & systèmes d’information - Usages et services numériques - Développement des services numériques + </div> + + <div + className="legal-notice-part" + > + <h3> + Crédits + </h3> + <p> + Ce site est le résultat de développements spécifiques réalisés dans les langages Go, TypeScript, HTML et Sass. Les développements s’appuient sur plusieurs bibliothèques et frameworks libres : axios, cozy-bar, cozy-client, cozy-harvest-lib, cozy-scripts, cozy-ui, d3, detect-browser, eslint-config-cozy-app, global, lodash, luxon, moment, moment-timezone, node-sass, object-hash, react, react-dom, react-lottie, react-router-dom, react-swipeable-views, sass-loader. La pile technique intègre également les applications Cozy stack, Yarn, Docker, ACH. Les déploiements sont réalisés sur le registre hébergé chez Cozy. L’équipe de réalisation utilise au quotidien les applications GitLab, IceScrum, RocketChat, Sonarqube. + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Traitement des données personnelles et droit d’accès, de modification et de suppression + </h3> + <p> + Conformément à la réglementation en vigueur en matière de protection des données personnelles, le service Ecolyo a fait l’objet d’une inscription au registre des traitements de la Métropole de Lyon. Ecolyo fait partie de l’écosystème de services orientés « self data » déployés par la Métropole de Lyon avec l’ambition d’offrir aux usagers métropolitains les outils et les services leur permettant d’exercer directement leur droit à la portabilité, dans un cadre apte à garantir aussi bien la transparence et le contrôle sur l’usage de leurs données personnelles que l’exploitation directe du contenu de ces données selon leurs libres choix. Le self data est en effet selon la Fondation Internet Nouvelle Génération (FING) « “la production, l’exploitation et le partage de données personnelles par les individus, sous leur contrôle et à leurs propres fins ». Au sein de cet environnement self data, la gestion des données s’appuie sur l’organisation suivante des rôles et responsabilités associées : + </p> + <ul> + <li> + Les partenaires du service Ecolyo – Enedis, GRDF et Eau du Grand Lyon sont responsable exclusivements des selis traitements de Données Personnelles relatifs à la collecte des données de consommation de l’usager et à leur transmission sur la plateforme de cloud personnel, après consentement de l’usager ; + </li> + <li> + La Métropole de Lyon est responsable de traitement sur le périmètre du service Ecolyo qu’elle propose à l’usager, ainsi que les traitements nécessaires à la fourniture de la plateforme de cloud personnel qu’elle met à disposition de l’usager pour accéder au service Ecolyo. En sa qualité de responsable de ces traitements, elle collecte et traite : + <ul> + <li> + Les données de compte de l’usager sur la plateforme de cloud personnel à des fins de gestion du compte et de communication avec l’usager ; + </li> + <li> + Les données privées de consommation d’énergie dont la récupération, la sauvegarde, le stockage, la synchronisation et le partage sur la plateforme de cloud personnel sont initiés par l’usager sans visibilité de la Métropole de Lyon sur leur contenu. + </li> + </ul> + </li> + <li> + L’usager, seul décisionnaire des finalités d’utilisation qu’il souhaite définir pour le traitement de ses données personnelles, à la suite de leur transmission par les partenaires du service Ecolyo sur son cloud personnel. + </li> + </ul> + </div> + <div + className="legal-notice-part" + > + <p> + Ainsi, dans le cadre de l’utilisation d’Ecolyo, l’usager ne recevra les données des partenaires du service : Enedis, GRDF et Eau du Grand Lyon seulement qu’à sa demande expresse après la saisie de ses identifiants. + </p> + <p> + L’utilisateur est donc le seul à accéder : + </p> + <ul> + <li> + À ses données de consommation d’électricité horaires, journalières, hebdomadaires, mensuelles et annuelles. + </li> + <li> + À ses données de consommation de gaz journalières, hebdomadaires, mensuelles et annuelles. + </li> + <li> + À ses données de consommation eau journalières, hebdomadaires, mensuelles et annuelles. + </li> + </ul> + <p> + Les engagements et responsabilités de la Métropole de Lyon concernant la protection des données et la confidentialité des données Ecolyo sont précisés dans les Mentions légales et les conditions d’utilisation du cloud personnel Grand Lyon qui accueille aujourd’hui le service Ecolyo et sans lequel le service ne peut pas fonctionner. Conformément à la loi 78-17 du 6 janvier 1978 modifiée relative à l’information, aux fichiers et aux libertés, vous disposez d’un droit d’accès, de rectification et d’opposition au traitement de vos données à caractère personnel. Votre cloud personnel vous permet d’exercer ces droits directement dans cet espace sur vos données de compte et sur vos données privées de consommation dans le cadre de votre utilisation du service Ecolyo. + </p> + <p> + Vous pouvez également si vous avez des questions à ce sujet, vous adresser par courrier postal à : + </p> + <p> + Métropole de Lyon - Direction des Affaires Juridiques et de la Commande Publique - 20, rue du Lac – CS 33569 - 69505 Lyon Cedex 03. + </p> + <p> + L’exercice des droits d’accès et de rectification de vos données nominatives auprès de la Métropole de Lyon concerne exclusivement celles de « Ecolyo ». + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Dispositions légales + </h3> + <p> + Les divers éléments du site web (la forme, la mise en page, le fonds, la structure …) sont protégés par le droit des dessins et modèles, le droit d’auteur, le droit des marques ainsi que le droit à l’image et ils ne peuvent être copiés ou imités en tout ou partie sauf autorisation expresse de la Métropole de Lyon. + </p> + + <p> + Toute personne ne respectant pas les dispositions légales applicables se rend coupable du délit de contrefaçon et est passible des sanctions pénales prévues par la loi. + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Droits d’auteurs + </h3> + <p> + Les photographies, textes, logos, pictogrammes, ainsi que toutes œuvres intégrées dans le site sont la propriété de la "Métropole de Lyon" ou de tiers ayant autorisé la "Métropole de Lyon" à les utiliser. + </p> + <p> + Les reproductions, les transmissions, les modifications, les réutilisations, sur un support papier ou informatique, dudit site et des œuvres qui y sont reproduites ne sont autorisées que pour un usage personnel et privé conforme aux dispositions de l’article L 122-5 du Code de la Propriété Intellectuelle. Ces reproductions devront ainsi notamment indiquer clairement la source et l’auteur du site et/ou de ces œuvres multimédias. En aucun cas ces reproductions ne sauraient porter préjudice aux droits des tiers. Les reproductions, les transmissions, les modifications, les réutilisations à des fins publicitaires, commerciales ou d’information, de tout ou partie du site, sont totalement interdites. + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Logo de la Métropole de Lyon et d’Ecolyo + </h3> + <p> + Les logos de la Métropole de Lyon et d’Ecolyo ne peuvent pas être modifiés – proportions, couleurs, éléments, constituants – et ne peuvent pas être sujet à aucune transformation, animation ou tout autre processus. Les logos de la Métropole de Lyon et d’Ecolyo ne peuvent être utilisés et associés qu’aux seules informations vérifiables. Ils ne peuvent être notamment utilisés que pour illustrer des relations avec la Métropole de Lyon ou des actions de sponsorings dûment établies. Quel que soit le cas d’espèce, la Métropole de Lyon se réserve le droit d’approuver ou de désapprouver toute utilisation des logos de la Métropole de Lyon et d’Ecolyo, pour assurer son utilisation correcte, conformément à l’éthique, la morale et aux intérêts de la Métropole de Lyon. Les conditions susmentionnées s’appliquent dans le cadre de pages web, elles ne font pas référence à l’utilisation des logos dans tout autre document. La Métropole de Lyon se réserve le droit de modifier les conditions d’utilisation des logos de la Métropole de Lyon et d’Ecolyo à tout moment et sans préavis. + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Droit des bases de données + </h3> + <p> + Les bases de données sont protégées par la loi du 1er juillet 1998 et le régime français du droit d’auteur. + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Établissements de liens vers le site Ecolyo + </h3> + <p> + La Métropole de Lyon autorise la mise en place d’un lien hypertexte vers son service Ecolyo pour tous les sites internet, à l’exclusion de ceux diffusant des informations à caractère polémique, pornographique, xénophobe ou pouvant, dans une plus large mesure porter atteinte à la sensibilité du plus grand nombre. + </p> + <p> + Le lien doit aboutir à la page d’accueil du service et le service Ecolyo doit apparaître dans une nouvelle fenêtre. Les pages du service Ecolyo ne doivent en aucun cas être intégrées à l’intérieur des pages d’un autre site (iframe). + </p> + <p> + Dans tous les cas d’espèce, La Métropole de Lyon se réserve le droit de demander la suppression d’un lien si elle estime que le site cible ne respecte pas les règles ainsi définies. + + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Liens vers des sites tiers depuis le site Ecolyo + </h3> + <p> + Les liens hypertextes mis en œuvre au sein du site en direction d’autres sites et/ou de pages personnelles et d’une manière générale vers toutes ressources existantes sur internet ne sauraient engager la responsabilité de la Métropole de Lyon quant aux liens qu’ils contiennent ou aux changements ou mises à jour qui leur sont apportés. + </p> + </div> + <div + className="legal-notice-part" + > + <h3> + Mise en garde générale + </h3> + <p> + Nos services mettent tout en œuvre pour offrir aux visiteurs de ce site web des informations fiables et vérifiées. Cependant, malgré tous les soins apportés, le site peut comporter des inexactitudes, des défauts de mise à jour ou des erreurs. + </p> + <p> + Nous remercions les utilisateurs du site de nous faire part d’éventuelles omissions, erreurs ou corrections par mail sur la boite aux lettres du webmestre. + </p> + </div> + </div> + </div> + </div> + </Content> +</React.Fragment> +`; diff --git a/src/components/Navbar/Navbar.spec.tsx b/src/components/Navbar/Navbar.spec.tsx index c206738ee6a5bf89eb32072a912e79580ae18690..dcf836b7859c6a14b288ca2af20cac1834197525 100644 --- a/src/components/Navbar/Navbar.spec.tsx +++ b/src/components/Navbar/Navbar.spec.tsx @@ -2,22 +2,35 @@ /* eslint-env jest */ import React from 'react' import { shallow } from 'enzyme' -import { RecoilRoot } from 'recoil' import Navbar from 'components/Navbar/Navbar' +import * as MockRecoil from '../../services/__mocks__/recoil' +import * as Recoil from 'recoil' -jest.mock('cozy-ui/transpiled/react/I18n', () => ({ - useI18n: jest.fn().mockResolvedValue({ - t: (str: string) => str, - }), -})) +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +jest.mock('recoil') +const { useRecoilValue } = (Recoil as unknown) as typeof MockRecoil describe('Navbar component', () => { - it('should be rendered correctly', () => { - const component = shallow( - <RecoilRoot> - <Navbar /> - </RecoilRoot> - ).getElement() + it('should be rendered correctly with notifications', () => { + useRecoilValue.mockReturnValue(true) + const context = { router: { isActive: () => true } } + const component = shallow(<Navbar />, { context }).getElement() + expect(component).toMatchSnapshot() + }) + + it('should be rendered correctly without notifications', () => { + useRecoilValue.mockReturnValue(false) + const context = { router: { isActive: () => true } } + const component = shallow(<Navbar />, { context }).getElement() expect(component).toMatchSnapshot() }) }) diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index b9bf9d03c82eb368e28297a11127ed5f875fe0e1..fbccff671ac123609b6a0a629ddd8a05efd0777f 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -21,7 +21,7 @@ import { reportNotificationState, } from 'atoms/notification.state' -export const Navbar = () => { +export const Navbar: React.FC = () => { const { t } = useI18n() const challengeNotification = useRecoilValue(challengeNotificationState) const reportNotification = useRecoilValue(reportNotificationState) diff --git a/src/components/Navbar/__snapshots__/Navbar.spec.tsx.snap b/src/components/Navbar/__snapshots__/Navbar.spec.tsx.snap index 8f4b6a8ea7631dc6df3632281e24628dd7798cde..fa5c19ea43d2f1205d526e97b11ebf3fa19f78fd 100644 --- a/src/components/Navbar/__snapshots__/Navbar.spec.tsx.snap +++ b/src/components/Navbar/__snapshots__/Navbar.spec.tsx.snap @@ -1,26 +1,253 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Navbar component should be rendered correctly 1`] = ` -<Context.Provider - value={ - Object { - "current": Object { - "addTransactionMetadata": [Function], - "getGraph": [Function], - "getState": [Function], - "replaceState": [Function], - "subscribeToTransactions": [Function], - }, - } - } +exports[`Navbar component should be rendered correctly with notifications 1`] = ` +<aside + className="o-sidebar" > - <Context.Provider - value={null} + <nav> + <ul + className="c-nav" + > + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/consumption" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.consumption + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/challenges" + > + <div + className="nb-notif" + > + 1 + </div> + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.challenges + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/ecogestures" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.ECOGESTURES + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/report" + > + <div + className="nb-notif" + > + 1 + </div> + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.report + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/options" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.options + </ForwardRef> + </li> + </ul> + </nav> + <div + className="logos-container" > - <Batcher - setNotifyBatcherOfChange={[Function]} + <StyledIcon + className="logo-grand-lyon" + icon="test-file-stub" + size={100} /> - <Navbar /> - </Context.Provider> -</Context.Provider> + <img + className="logo-tiga" + src="test-file-stub" + /> + </div> +</aside> +`; + +exports[`Navbar component should be rendered correctly without notifications 1`] = ` +<aside + className="o-sidebar" +> + <nav> + <ul + className="c-nav" + > + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/consumption" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.consumption + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/challenges" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.challenges + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/ecogestures" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.ECOGESTURES + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/report" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.report + </ForwardRef> + </li> + <li + className="c-nav-item" + > + <ForwardRef + activeClassName="is-active" + className="c-nav-link" + to="/options" + > + <StyledIcon + className="c-nav-icon off" + icon="test-file-stub" + /> + <StyledIcon + className="c-nav-icon on" + icon="test-file-stub" + /> + Nav.options + </ForwardRef> + </li> + </ul> + </nav> + <div + className="logos-container" + > + <StyledIcon + className="logo-grand-lyon" + icon="test-file-stub" + size={100} + /> + <img + className="logo-tiga" + src="test-file-stub" + /> + </div> +</aside> `; diff --git a/src/services/__mocks__/challengesTypeData.json b/src/services/__mocks__/challengesTypeData.json new file mode 100644 index 0000000000000000000000000000000000000000..c2d0908b0f92eb165186fb9b819afeb9211448ee --- /dev/null +++ b/src/services/__mocks__/challengesTypeData.json @@ -0,0 +1,404 @@ +[ + { + "id": "CHA00000001", + "_id": "CHA00000001", + "_type": "com.grandlyon.ecolyo.challengetype", + "_rev": "1-ae99679ff8a70a4de91071c9310df415", + "cozyMetadata": { + "createdAt": "2020-11-03T08:56:10.479Z", + "createdByApp": "ecolyo", + "createdByAppVersion": "0.2.1", + "metadataVersion": 1, + "updatedAt": "2020-11-03T08:56:10.479Z", + "updatedByApps": [ + { + "date": "2020-11-03T08:56:10.479Z", + "slug": "ecolyo", + "version": "0.2.1" + } + ] + }, + "description": "Connecter l'application Ecolyo à un distributeur d'énergie", + "duration": { + "days": 0 + }, + "fluidTypes": [0, 1, 2], + "level": 1, + "relationships": { + "availableEcogestures": { + "data": [ + { + "_id": "0085", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0092", + "_type": "com.grandlyon.ecolyo.ecogesture" + } + ] + } + }, + "title": "Ecolyo Royal", + "type": 1 + }, + { + "id": "CHA00000002", + "_id": "CHA00000002", + "_type": "com.grandlyon.ecolyo.challengetype", + "_rev": "1-d38ff490140e8b18829d88791d987938", + "cozyMetadata": { + "createdAt": "2020-11-03T08:56:10.541Z", + "createdByApp": "ecolyo", + "createdByAppVersion": "0.2.1", + "metadataVersion": 1, + "updatedAt": "2020-11-03T08:56:10.541Z", + "updatedByApps": [ + { + "date": "2020-11-03T08:56:10.541Z", + "slug": "ecolyo", + "version": "0.2.1" + } + ] + }, + "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers ?", + "duration": { + "days": 7 + }, + "fluidTypes": [0, 1, 2], + "level": 2, + "relationships": { + "availableEcogestures": { + "data": [ + { + "_id": "0032", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0034", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0041", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0042", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0043", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0045", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0050", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0058", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0064", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0066", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0071", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0078", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0082", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0093", + "_type": "com.grandlyon.ecolyo.ecogesture" + } + ] + } + }, + "title": "Coques en stock", + "type": 0 + }, + { + "id": "CHA00000003", + "_id": "CHA00000003", + "_type": "com.grandlyon.ecolyo.challengetype", + "_rev": "1-6cef2075f6e074123c836c69653423d6", + "cozyMetadata": { + "createdAt": "2020-11-03T08:56:10.571Z", + "createdByApp": "ecolyo", + "createdByAppVersion": "0.2.1", + "metadataVersion": 1, + "updatedAt": "2020-11-03T08:56:10.571Z", + "updatedByApps": [ + { + "date": "2020-11-03T08:56:10.571Z", + "slug": "ecolyo", + "version": "0.2.1" + } + ] + }, + "description": "Et si dans les 4 prochaines semaines vous réussissiez à consommer moins que dans les 4 dernières ?", + "duration": { + "days": 28 + }, + "fluidTypes": [0, 1, 2], + "level": 3, + "relationships": { + "availableEcogestures": { + "data": [ + { + "_id": "0032", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0034", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0041", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0042", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0043", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0045", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0050", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0058", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0064", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0066", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0071", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0078", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0082", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0093", + "_type": "com.grandlyon.ecolyo.ecogesture" + } + ] + } + }, + "title": "Méga Coques en stock", + "type": 0 + }, + { + "id": "CHA00000004", + "_id": "CHA00000004", + "_type": "com.grandlyon.ecolyo.challengetype", + "_rev": "1-cabdfd17ee34fa66628055be718668e5", + "cozyMetadata": { + "createdAt": "2020-11-03T08:56:10.606Z", + "createdByApp": "ecolyo", + "createdByAppVersion": "0.2.1", + "metadataVersion": 1, + "updatedAt": "2020-11-03T08:56:10.606Z", + "updatedByApps": [ + { + "date": "2020-11-03T08:56:10.606Z", + "slug": "ecolyo", + "version": "0.2.1" + } + ] + }, + "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers", + "duration": { + "days": 7 + }, + "fluidTypes": [0, 1, 2], + "level": 901, + "relationships": { + "availableEcogestures": { + "data": [ + { + "_id": "0032", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0034", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0041", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0042", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0043", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0045", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0050", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0058", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0064", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0066", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0071", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0078", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0082", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0093", + "_type": "com.grandlyon.ecolyo.ecogesture" + } + ] + } + }, + "title": "Winter is leaving", + "type": 0 + }, + { + "id": "CHA00000005", + "_id": "CHA00000005", + "_type": "com.grandlyon.ecolyo.challengetype", + "_rev": "1-c847d478d7255da115dc0403f48d370a", + "cozyMetadata": { + "createdAt": "2020-11-03T08:56:10.633Z", + "createdByApp": "ecolyo", + "createdByAppVersion": "0.2.1", + "metadataVersion": 1, + "updatedAt": "2020-11-03T08:56:10.633Z", + "updatedByApps": [ + { + "date": "2020-11-03T08:56:10.633Z", + "slug": "ecolyo", + "version": "0.2.1" + } + ] + }, + "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers", + "duration": { + "days": 7 + }, + "fluidTypes": [0, 1, 2], + "level": 902, + "relationships": { + "availableEcogestures": { + "data": [ + { + "_id": "0032", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0034", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0041", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0042", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0043", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0045", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0050", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0058", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0064", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0066", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0071", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0078", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0082", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "_id": "0093", + "_type": "com.grandlyon.ecolyo.ecogesture" + } + ] + } + }, + "title": "Méga Winter is leaving", + "type": 0 + } +] diff --git a/src/services/__mocks__/ecogesturesNotUpToDateData.json b/src/services/__mocks__/ecogesturesNotUpToDateData.json new file mode 100644 index 0000000000000000000000000000000000000000..b6a5eb1f1d5c5ddf71c6cbcac2d276692b7e4d93 --- /dev/null +++ b/src/services/__mocks__/ecogesturesNotUpToDateData.json @@ -0,0 +1,30 @@ +[ + { + "fluidTypes": [0], + "id": "0032", + "longDescription": "Bien souvent les chauffe-eau sont réglés pour une température de 70 °C dans le ballon, ce qui n’est pas économique et peut même être dangereux ! Au-dessus de 60 °C vous augmentez aussi l’entartrage. L’eau ne doit pas être trop froide non plus pour éviter le risque de prolifération de micro-organismes comme les légionelles. A noter : parfois l’accès au réglage n’est pas directement visible… il faut alors retirer le capot en plastique situé sous le chauffe-eau pour y accéder.", + "longName": "Je règle la température de mon eau chaude entre 55 et 60 °C.", + "nwh": 6, + "pack": 8, + "shortDescription": "", + "shortName": "Contrôle du nuage", + "usage": "Eau chaude sanitaire", + "_id": "0032", + "_rev": "1-67f1ea36efdd892c96bf64a8943154cd", + "_type": "com.grandlyon.ecolyo.ecogesture" + }, + { + "fluidTypes": [1], + "id": "0034", + "longDescription": "Si on n'a pas transpiré la journée et qu'on ne sent pas mauvais, aucune obligation de se laver tous les jours. Il est même conseillé pour la flore de l'épiderme (la fameuse barrière contre certains pathogènes) de ne pas se laver tous les jours. Vous économiserez ainsi une quantité d'eau non négligeable.", + "longName": "Je décide de supprimer une douche par semaine.", + "nwh": 5, + "pack": 6, + "shortDescription": "", + "shortName": "Vague de sécheresse", + "usage": "Eau chaude sanitaire", + "_id": "0034", + "_rev": "1-ef7ddd778254e3b7d331a88fd17f606d", + "_type": "com.grandlyon.ecolyo.ecogesture" + } +] diff --git a/src/services/__mocks__/recoil.ts b/src/services/__mocks__/recoil.ts new file mode 100644 index 0000000000000000000000000000000000000000..31355bbbdc5a851f6ed77885b33162d556a130d1 --- /dev/null +++ b/src/services/__mocks__/recoil.ts @@ -0,0 +1,4 @@ +export const atom = jest.fn() +export const selector = jest.fn() +export const useRecoilValue = jest.fn() +export const useSetRecoilState = jest.fn() diff --git a/src/services/initialization.service.spec.ts b/src/services/initialization.service.spec.ts index bde3373d30f4cc6fee67635480b9d4e97f2b221f..ef000eb42480d79455f4eda16d026f54b4510332 100644 --- a/src/services/initialization.service.spec.ts +++ b/src/services/initialization.service.spec.ts @@ -1,9 +1,18 @@ import { QueryResult } from 'cozy-client' import { DateTime } from 'luxon' -import { UserProfile } from 'models' +import { UserProfile, Ecogesture } from 'models' import InitializationService from './initialization.service' import mockClient from './__mocks__/client' import userProfileEntityData from './__mocks__/userProfile.json' +import ecogesturesEntityData from './__mocks__/ecogesturesData.json' +import ecogesturesNotUpToDateEntityData from './__mocks__/ecogesturesNotUpToDateData.json' +import challengesTypeEntityData from './__mocks__/challengesTypeData.json' + +// => à décommenter quand les fichiers mock manquants sont ajoutés +//import userChallengesEntityData from './__mocks__/userChallengesData.json' +//import konnectorAccountStatusEntityData from './__mocks__/konnectorAccountStatusData.json' +//import fluidStatusEntityData from './__mocks__/fluidStatusData.json' +//import currentChallengeEntityData from './__mocks__/currentChallengeData.json' const mockCreateIndexKonnector = jest.fn() jest.mock('./konnector.service', () => { @@ -32,6 +41,69 @@ jest.mock('./userProfile.service', () => { }) }) +const mockGetAllEcogestures = jest.fn() +jest.mock('./ecogesture.service', () => { + return jest.fn(() => { + return { + getAllEcogestures: mockGetAllEcogestures, + } + }) +}) + +const mockGetAllChallengeTypeEntities = jest.fn() +jest.mock('./challenge.service', () => { + return jest.fn(() => { + return { + getAllChallengeTypeEntities: mockGetAllChallengeTypeEntities, + } + }) +}) + +const mockGetActualReportDate = jest.fn() +jest.mock('../utils/date', () => { + return jest.fn(() => { + return { + getActualReportDate: mockGetActualReportDate, + } + }) +}) + +const mockGetAllUserChallenges = jest.fn() +jest.mock('./challenge.service', () => { + return jest.fn(() => { + return { + getAllUserChallenges: mockGetAllUserChallenges, + } + }) +}) + +const mockGetKonnectorAccountStatus = jest.fn() +jest.mock('./konnectorStatus.service', () => { + return jest.fn(() => { + return { + getKonnectorAccountStatus: mockGetKonnectorAccountStatus, + } + }) +}) + +const mockGetFluidStatus = jest.fn() +jest.mock('./fluid.service', () => { + return jest.fn(() => { + return { + getFluidStatus: mockGetFluidStatus, + } + }) +}) + +const mockGetCurrentChallenge = jest.fn() +jest.mock('./fluid.service', () => { + return jest.fn(() => { + return { + getCurrentChallenge: mockGetCurrentChallenge, + } + }) +}) + const userProfileData = { ...userProfileEntityData, report: { @@ -42,9 +114,13 @@ const userProfileData = { }, } +//Test initialisation + describe('Initialization service', () => { const initializationService = new InitializationService(mockClient) + //Test initIndex + describe('initIndex method', () => { it('shoud return true when all indexes created', async () => { const mockQueryResult: QueryResult<boolean> = { @@ -80,6 +156,8 @@ describe('Initialization service', () => { }) }) + //Test initUserProfile + describe('initUserProfile method', () => { it('shoud return the userProfil when existing', async () => { mockGetUserProfile.mockResolvedValue(userProfileData) @@ -151,4 +229,113 @@ describe('Initialization service', () => { expect(error).toEqual(new Error()) }) }) + + // test initChallengeType + //////////// non terminé, à faire //////////////// + + describe('initChallengeType method', () => { + //Ok + it('shoud return true and userProfile when challengeType is already up to date', async () => { + mockGetAllChallengeTypeEntities.mockResolvedValue( + challengesTypeEntityData + ) + const result: { + result: boolean + userProfile: UserProfile | null + } = await initializationService.initChallengeType( + userProfileData.challengeTypeHash + ) + expect(result).toEqual({ result: true, userProfile: null }) + }) + }) + + //test initEcoGesture + ///////////// non terminé, à faire ///////// + + describe('initEcoGesture method', () => { + //Non ok + it('shoud return true and userProfile ecogestures list is not loaded', async () => { + mockGetAllEcogestures.mockResolvedValue(false) + const result: { + result: boolean + userProfile: UserProfile | null + } = await initializationService.initEcogesture( + userProfileData.ecogestureHash + ) + expect(result).toEqual({ result: true, userProfile: null }) + }) + + //Non ok + it('shoud return true and userProfile when ecogestures list is empty', async () => { + mockGetAllEcogestures.mockResolvedValue({}) + const result: { + result: boolean + userProfile: UserProfile | null + } = await initializationService.initEcogesture( + userProfileData.ecogestureHash + ) + expect(result).toEqual({ result: true, userProfile: null }) + }) + + //OK + it('shoud return true and userProfile = null when ecogestures hash is already up to date', async () => { + mockGetAllEcogestures.mockResolvedValue(ecogesturesEntityData) + const result: { + result: boolean + userProfile: UserProfile | null + } = await initializationService.initEcogesture( + userProfileData.ecogestureHash + ) + expect(result).toEqual({ result: true, userProfile: null }) + }) + + //Non ok + it('shoud return true and userProfile when ecogesture hash is not up to date', async () => { + mockGetAllEcogestures.mockResolvedValue(ecogesturesNotUpToDateEntityData) + const result: { + result: boolean + userProfile: UserProfile | null + } = await initializationService.initEcogesture( + userProfileData.ecogestureHash + ) + expect(result).toEqual({ result: true, userProfile: null }) + }) + }) + + //test initReport + // à faire + describe('initReport method', () => { + it('') + }) + + //test initUserChallenge + ////// il manque le fichier .json userChallenge + describe('initUserChallenge method', () => { + //mockGetAllUserChallenges.mockResolvedValue(userChallengesEntityData) + it('') + }) + + //test initFluidType + /////// il manque le fichier .json konnectorAccountStatus + describe('initFluidType method', () => { + //mockGetKonnectorAccountStatus.mockResolvedValue(konnectorAccountStatusEntityData) + it('') + }) + + //test initFluidStatus + describe('initFluidStatus method', () => { + //mockGetFluidStatus.mockResolvedValue(fluidStatusEntityData) + it('') + }) + + //test initCurrentChallenge + //////// il manque le fichier mock currentChallenge + describe('initCurrentChallenge method', () => { + //mockGetCurrentChallenge.mockResolvedValue(currentChallengeEntityData) + it('') + }) + + //test isCurrentChallengeOver + + //test checkAchivement })