Browse Source

[feat] Add E2E testing

Sergio Mattei 1 year ago
parent
commit
5a7212624d

+ 10
- 0
cypress.config.ts View File

@@ -0,0 +1,10 @@
1
+import { defineConfig } from "cypress";
2
+
3
+export default defineConfig({
4
+  e2e: {
5
+    baseUrl: "http://localhost:3001",
6
+    setupNodeEvents(on, config) {
7
+      // implement node event listeners here
8
+    },
9
+  },
10
+});

+ 2
- 0
cypress/e2e/eduardo.cy.ts View File

@@ -0,0 +1,2 @@
1
+// I will E2E test the article page.
2
+// User visits home -> User clicks on "laws or advice" -> List renders with 200

+ 2
- 0
cypress/e2e/eliam.cy.ts View File

@@ -0,0 +1,2 @@
1
+// I will E2E test the article page.
2
+// User visits home -> User clicks on "laws or advice" -> Article page contains the title

+ 50
- 0
cypress/e2e/sergio.cy.ts View File

@@ -0,0 +1,50 @@
1
+describe("Our app", () => {
2
+  beforeEach(() => {
3
+    cy.intercept("GET", "http://localhost:3000/api/getTopicList/*", {
4
+      fixture: "topics",
5
+    }).as("getTopicList");
6
+
7
+    cy.intercept("GET", "http://localhost:3000/api/getTopic/*", {
8
+      fixture: "article",
9
+    }).as("getTopic");
10
+
11
+    cy.viewport("iphone-x");
12
+    cy.visit("/");
13
+  });
14
+
15
+  it("shows the home page", () => {
16
+    cy.visit("/");
17
+    cy.contains("Bienvenidos");
18
+  });
19
+
20
+  it("shows a video in the home page", () => {
21
+    cy.visit("/");
22
+    cy.get("iframe").should("be.visible");
23
+  });
24
+
25
+  it("shows the about page", () => {
26
+    cy.visit("/about");
27
+    cy.contains("Renacer Social");
28
+  });
29
+
30
+  it("shows the individual service pages", () => {
31
+    cy.visit("/about");
32
+    cy.get(".item").first().click();
33
+    cy.contains("Casita de Paz");
34
+  });
35
+
36
+  it("shows the advice pages", () => {
37
+    cy.visit("/advice");
38
+    // Wait until the request to the intercept is completed.
39
+    cy.wait("@getTopicList");
40
+    cy.get(".item").first().click();
41
+    cy.wait("@getTopic");
42
+  });
43
+
44
+  it("allows exiting the individual service pages", () => {
45
+    cy.visit("/about");
46
+    cy.get(".item").first().click();
47
+    cy.get(".back-button-has-icon-only").first().click();
48
+    cy.contains("Servicios");
49
+  });
50
+});

+ 1643
- 0
cypress/fixtures/article.json
File diff suppressed because it is too large
View File


+ 14
- 0
cypress/fixtures/topics.json View File

@@ -0,0 +1,14 @@
1
+[
2
+  {
3
+    "blockId": "07ffbf26-babe-4c6a-b046-c7eb6d5c1a7f",
4
+    "name": "Pasa tiempo con tus hijos"
5
+  },
6
+  {
7
+    "blockId": "754d8ef3-14ae-4af6-a8c3-4ac4ede4d49d",
8
+    "name": "10 consejos para un matrimonio feliz"
9
+  },
10
+  {
11
+    "blockId": "d9fbaf7c-8f89-43b3-945c-927ed90e098b",
12
+    "name": "Busca ayuda si eres abusado/abusada"
13
+  }
14
+]

+ 37
- 0
cypress/support/commands.ts View File

@@ -0,0 +1,37 @@
1
+/// <reference types="cypress" />
2
+// ***********************************************
3
+// This example commands.ts shows you how to
4
+// create various custom commands and overwrite
5
+// existing commands.
6
+//
7
+// For more comprehensive examples of custom
8
+// commands please read more here:
9
+// https://on.cypress.io/custom-commands
10
+// ***********************************************
11
+//
12
+//
13
+// -- This is a parent command --
14
+// Cypress.Commands.add('login', (email, password) => { ... })
15
+//
16
+//
17
+// -- This is a child command --
18
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
19
+//
20
+//
21
+// -- This is a dual command --
22
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
23
+//
24
+//
25
+// -- This will overwrite an existing command --
26
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
27
+//
28
+// declare global {
29
+//   namespace Cypress {
30
+//     interface Chainable {
31
+//       login(email: string, password: string): Chainable<void>
32
+//       drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
33
+//       dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
34
+//       visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
35
+//     }
36
+//   }
37
+// }

+ 20
- 0
cypress/support/e2e.ts View File

@@ -0,0 +1,20 @@
1
+// ***********************************************************
2
+// This example support/e2e.ts is processed and
3
+// loaded automatically before your test files.
4
+//
5
+// This is a great place to put global configuration and
6
+// behavior that modifies Cypress.
7
+//
8
+// You can change the location of this file or turn off
9
+// automatically serving support files with the
10
+// 'supportFile' configuration option.
11
+//
12
+// You can read more here:
13
+// https://on.cypress.io/configuration
14
+// ***********************************************************
15
+
16
+// Import commands.js using ES2015 syntax:
17
+import "./commands";
18
+
19
+// Alternatively you can use CommonJS syntax:
20
+// require('./commands')

+ 1803
- 36
package-lock.json
File diff suppressed because it is too large
View File


+ 2
- 1
package.json View File

@@ -48,7 +48,7 @@
48 48
     "workbox-streams": "^5.1.4"
49 49
   },
50 50
   "scripts": {
51
-    "start": "react-scripts start",
51
+    "start": "PORT=3001 react-scripts start",
52 52
     "build": "react-scripts build",
53 53
     "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
54 54
     "eject": "react-scripts eject",
@@ -76,6 +76,7 @@
76 76
     "@capacitor/cli": "4.4.0",
77 77
     "@ionic/lab": "3.2.15",
78 78
     "@types/lodash": "^4.14.191",
79
+    "cypress": "^12.1.0",
79 80
     "husky": "^8.0.0",
80 81
     "pretty-quick": "^3.1.3"
81 82
   },

+ 2
- 0
src/components/TopicList.tsx View File

@@ -32,6 +32,7 @@ const TopicList: React.FC<{
32 32
   topics: Topic[];
33 33
   loading?: boolean;
34 34
 }> = ({ title, topics, loading }) => {
35
+  console.log(topics);
35 36
   return (
36 37
     <IonList>
37 38
       {title ? (
@@ -43,6 +44,7 @@ const TopicList: React.FC<{
43 44
       {loading && <ListSkeleton />}
44 45
 
45 46
       {!loading &&
47
+        topics &&
46 48
         topics.map((topic: Topic) => {
47 49
           return (
48 50
             <IonItem

+ 36
- 27
src/pages/AboutListPage.tsx View File

@@ -1,8 +1,11 @@
1 1
 import {
2 2
   IonContent,
3 3
   IonHeader,
4
+  IonImg,
4 5
   IonItem,
6
+  IonLabel,
5 7
   IonList,
8
+  IonListHeader,
6 9
   IonPage,
7 10
   IonTitle,
8 11
   IonToolbar,
@@ -22,37 +25,43 @@ const AboutListPage: React.FC = () => {
22 25
             <IonTitle size="large">About</IonTitle>
23 26
           </IonToolbar>
24 27
         </IonHeader>
25
-        <div className="ion-padding">
28
+        <IonContent className="ion-padding">
29
+          <div className="video">
30
+            <iframe
31
+              src="https://www.youtube.com/embed/MRo8FR2l6VM"
32
+              title="YouTube video player"
33
+              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
34
+            ></iframe>
35
+          </div>
26 36
           <h5>Bienvenidos</h5>
27 37
           <p>
28
-            Aqui tenemos una lista de las cosas que Renacer Social ofrece a la
29
-            sociedad.
38
+            Renacer Social ha iniciado una campaña de orientación a la comunidad
39
+            para que los niños y niñas puedan denunciar su situación de
40
+            maltrato. Con una seña discreta que consta de una “X” formada frente
41
+            al torso con los dedos índice, el menor puede dar alerta de su
42
+            situación de maltrato. Es responsabilidad de todos denunciar la
43
+            posible situación de maltrato si vemos a un menor realizar dicha
44
+            seña.
30 45
           </p>
31
-        </div>
46
+          <IonList>
47
+            <IonListHeader>
48
+              <IonLabel>Servicios</IonLabel>
49
+            </IonListHeader>
50
+            <IonItem routerLink={`/services/casita`}>
51
+              <p className="ListItemText">Casita de Paz</p>
52
+            </IonItem>
53
+            <IonItem routerLink={`/services/Crecemos`}>
54
+              <p className="ListItemText">Crecemos</p>
55
+            </IonItem>
56
+            <IonItem routerLink={`/services/Lazos`}>
57
+              <p className="ListItemText">Proyecto Lazos</p>
58
+            </IonItem>
59
+            <IonItem routerLink={`/services/Supervisadas`}>
60
+              <p className="ListItemText">Visitas Supervisadas</p>
61
+            </IonItem>
62
+          </IonList>
63
+        </IonContent>
32 64
       </IonContent>
33
-      <IonList>
34
-        <IonItem routerLink={`/services/casita`}>
35
-          <p className="ListItemText">Casita de Paz</p>
36
-        </IonItem>
37
-      </IonList>
38
-
39
-      <IonList>
40
-        <IonItem routerLink={`/services/Crecemos`}>
41
-          <p className="ListItemText">Crecemos</p>
42
-        </IonItem>
43
-      </IonList>
44
-
45
-      <IonList>
46
-        <IonItem routerLink={`/services/Lazos`}>
47
-          <p className="ListItemText">Proyecto Lazos</p>
48
-        </IonItem>
49
-      </IonList>
50
-
51
-      <IonList>
52
-        <IonItem routerLink={`/services/Supervisadas`}>
53
-          <p className="ListItemText">Visitas Supervisadas</p>
54
-        </IonItem>
55
-      </IonList>
56 65
     </IonPage>
57 66
   );
58 67
 };

+ 3
- 3
src/pages/AdviceListPage.tsx View File

@@ -14,15 +14,15 @@ import { Topic } from "../types";
14 14
 
15 15
 const AdviceListPage: React.FC = () => {
16 16
   const { data: paternidad, error: paternidadError } = useSWR<Topic[]>(
17
-    "/api/getTopicList?type=advice&category=Paternidad",
17
+    "/api/getTopicList/?type=advice&category=Paternidad",
18 18
     fetcher
19 19
   );
20 20
   const { data: matrimonio, error: matrimonioError } = useSWR<Topic[]>(
21
-    "/api/getTopicList?type=advice&category=Matrimonio",
21
+    "/api/getTopicList/?type=advice&category=Matrimonio",
22 22
     fetcher
23 23
   );
24 24
   const { data: abuso, error: abusoError } = useSWR<Topic[]>(
25
-    "/api/getTopicList?type=advice&category=Abuso%20Sexual",
25
+    "/api/getTopicList/?type=advice&category=Abuso%20Sexual",
26 26
     fetcher
27 27
   );
28 28
   const error = paternidadError || matrimonioError || abusoError;

+ 1
- 1
src/pages/ArticlePage.tsx View File

@@ -23,7 +23,7 @@ const ArticlePage: React.FC<RouteComponentProps<{ articleId: string }>> = ({
23 23
   match,
24 24
 }) => {
25 25
   const { data, error } = useSWR<any>(
26
-    `/api/getTopic?id=${match.params.articleId}`,
26
+    `/api/getTopic/?id=${match.params.articleId}`,
27 27
     fetcher
28 28
   );
29 29
   const isLoading = !data && !error;

+ 15
- 1
src/pages/Home.tsx View File

@@ -21,11 +21,25 @@ const Home: React.FC = () => {
21 21
           </IonToolbar>
22 22
         </IonHeader>
23 23
         <div className="ion-padding">
24
-          <h5>Bienvenidos</h5>
24
+          <div className="video">
25
+            <iframe
26
+              src="https://www.youtube.com/embed/xCtgekNpxI4"
27
+              title="YouTube video player"
28
+              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
29
+            ></iframe>
30
+          </div>
31
+          <h2>Bienvenidos</h2>
25 32
           <p>
26 33
             Somos una organización Lorem Ipsum is simply dummy text of the
27 34
             printing and typesetting industry. Lorem Ipsum.
28 35
           </p>
36
+
37
+          <h3>Si se encuentra en peligro llame al 9-1-1 o al 787-343-2020</h3>
38
+          <p>
39
+            Si usted se encuentra en peligro inminente, llame a la Policía de
40
+            Puerto Rico al 9-1-1 o al 787-343-2020. Debe buscar protección para
41
+            usted y los suyos de manera inmediata. NO ESPERE.
42
+          </p>
29 43
         </div>
30 44
       </IonContent>
31 45
     </IonPage>

+ 0
- 1
src/pages/LawListPage.tsx View File

@@ -1,7 +1,6 @@
1 1
 import {
2 2
   IonContent,
3 3
   IonHeader,
4
-  IonLoading,
5 4
   IonPage,
6 5
   IonTitle,
7 6
   IonToast,

+ 18
- 0
src/theme/global.css View File

@@ -17,3 +17,21 @@ main.notion-page-no-cover {
17 17
 .notion-blank {
18 18
   display: none !important;
19 19
 }
20
+
21
+.video {
22
+  position: relative;
23
+  overflow: hidden;
24
+  width: 100%;
25
+  padding-top: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
26
+}
27
+
28
+/* Then style the iframe to fit in the container div with full height and width */
29
+.video iframe {
30
+  position: absolute;
31
+  top: 0;
32
+  left: 0;
33
+  bottom: 0;
34
+  right: 0;
35
+  width: 100%;
36
+  height: 100%;
37
+}