From bf23ff4bd1ef8e4450d688f44206f60a7f707cc5 Mon Sep 17 00:00:00 2001 From: alyu201 Date: Tue, 8 Mar 2022 22:21:18 +1300 Subject: [PATCH 01/26] Add link sharing modal component --- .vscode/rtse.code-snippets | 33 +++++++ .../src/components/ShareLinkModal.tsx | 89 +++++++++++++++++++ .../frontend/src/pages/public/HomePage.tsx | 27 +++++- 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 .vscode/rtse.code-snippets create mode 100644 packages/frontend/src/components/ShareLinkModal.tsx diff --git a/.vscode/rtse.code-snippets b/.vscode/rtse.code-snippets new file mode 100644 index 00000000..0dcaaf76 --- /dev/null +++ b/.vscode/rtse.code-snippets @@ -0,0 +1,33 @@ +{ + // Place your platform workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and + // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope + // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is + // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: + // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. + // Placeholders with the same ids are connected. + // Example: + // "Print to console": { + // "scope": "javascript,typescript", + // "prefix": "log", + // "body": [ + // "console.log('$1');", + // "$2" + // ], + // "description": "Log output to console" + // } + "Create a typescript default export functional react component": { + "prefix": "rtse", + "body": [ + "import React from \"react\"", + "", + "interface ${1:$TM_FILENAME_BASE}Props {}", + "", + "const ${1:$TM_FILENAME_BASE}: React.FC<${1:$TM_FILENAME_BASE}Props> = () => {", + " $0", + " return
", + "}", + "", + "export default $TM_FILENAME_BASE" + ] + } +} diff --git a/packages/frontend/src/components/ShareLinkModal.tsx b/packages/frontend/src/components/ShareLinkModal.tsx new file mode 100644 index 00000000..8d620b40 --- /dev/null +++ b/packages/frontend/src/components/ShareLinkModal.tsx @@ -0,0 +1,89 @@ +import { + Avatar, + Button, + Container, + Input, + Loading, + Modal, + Text, +} from '@nextui-org/react'; +import React from 'react'; +import { DuplicateIcon, LinkIcon } from '@heroicons/react/outline'; +import { UserAddIcon } from '@heroicons/react/solid'; + +interface ShareLinkModalProps { + visible: boolean; + setVisible(value: boolean): void; +} + +const ShareLinkModal: React.FC = ({ + visible, + setVisible, +}) => { + // TODO: change to valid api call =============== + const getHouseCode = () => { + return { data: 'house12345678' }; + }; + const { data: code } = getHouseCode(); + + const copyButton = () => { + return ( + } + onClick={() => console.log('copied!')} + css={{ p: 10 }} + as="button" + /> + ); + }; + // ============================================== + + const closeHandler = () => { + setVisible(false); + }; + + return ( +
+ + + + } + color="primary" + /> + + Invite flatmates! + + + + + } + contentRight={copyButton()} + /> + + + + + +
+ ); +}; + +export default ShareLinkModal; diff --git a/packages/frontend/src/pages/public/HomePage.tsx b/packages/frontend/src/pages/public/HomePage.tsx index 3570dbfb..ca994eb8 100644 --- a/packages/frontend/src/pages/public/HomePage.tsx +++ b/packages/frontend/src/pages/public/HomePage.tsx @@ -1,11 +1,22 @@ -import { Button, Text } from '@nextui-org/react'; -import React from 'react'; +import { Button, Spacer, Text } from '@nextui-org/react'; +import React, { useState } from 'react'; import { useApi } from '../../hooks/useApi'; import { useAuth } from '../../hooks/useAuth'; +import ShareLinkModal from '../../components/ShareLinkModal'; +import { LinkIcon } from '@heroicons/react/outline'; interface HomePageProps {} +// TODO: file to be reverted back and changes to be moved to dashboard feature const HomePage: React.FC = () => { + console.log(window.location.href); + // Logic to be moved ================================ + const [visible, setVisible] = useState(false); + const onClick = () => { + setVisible(true); + }; + // ================================================== + const { data } = useApi('/api/v1/ping', { method: 'get', }); @@ -20,6 +31,18 @@ const HomePage: React.FC = () => { Hello

{user.displayName}

+ {/* Logic to be moved ==========================*/} + + + + {/* ============================================*/} ); }; From fd15866a8aa1d29c3441083ad6d64eca48e56a19 Mon Sep 17 00:00:00 2001 From: alyu201 Date: Wed, 9 Mar 2022 00:45:54 +1300 Subject: [PATCH 02/26] Added copy link to clipboard function --- .../src/components/ShareLinkModal.tsx | 62 +++++++++++++------ .../frontend/src/pages/public/HomePage.tsx | 1 - 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/packages/frontend/src/components/ShareLinkModal.tsx b/packages/frontend/src/components/ShareLinkModal.tsx index 8d620b40..7d58b0e7 100644 --- a/packages/frontend/src/components/ShareLinkModal.tsx +++ b/packages/frontend/src/components/ShareLinkModal.tsx @@ -3,12 +3,11 @@ import { Button, Container, Input, - Loading, Modal, Text, } from '@nextui-org/react'; -import React from 'react'; -import { DuplicateIcon, LinkIcon } from '@heroicons/react/outline'; +import React, { useState } from 'react'; +import { DuplicateIcon, LinkIcon, CheckIcon } from '@heroicons/react/outline'; import { UserAddIcon } from '@heroicons/react/solid'; interface ShareLinkModalProps { @@ -25,23 +24,35 @@ const ShareLinkModal: React.FC = ({ return { data: 'house12345678' }; }; const { data: code } = getHouseCode(); + // ============================================== + + const link = `${window.location.origin}?join=${code}`; + const [copied, setCopied] = useState(false); + const [icon, setIcon] = useState(); + const copyHandler = () => { + setCopied(true); + navigator.clipboard.writeText(link); + setIcon(); + setTimeout(setIcon, 1500, ); + }; + + const closeHandler = () => { + setVisible(false); + setCopied(false); + }; const copyButton = () => { return ( } - onClick={() => console.log('copied!')} + squared + icon={icon} + onClick={copyHandler} css={{ p: 10 }} as="button" + pointer /> ); }; - // ============================================== - - const closeHandler = () => { - setVisible(false); - }; return (
@@ -68,15 +79,30 @@ const ShareLinkModal: React.FC = ({ - } - contentRight={copyButton()} - /> + + } + width="86%" + /> + {copyButton()} + + {copied ? ( + + Copied! + + ) : null} + diff --git a/packages/frontend/src/pages/public/HomePage.tsx b/packages/frontend/src/pages/public/HomePage.tsx index ca994eb8..9d935fb5 100644 --- a/packages/frontend/src/pages/public/HomePage.tsx +++ b/packages/frontend/src/pages/public/HomePage.tsx @@ -9,7 +9,6 @@ interface HomePageProps {} // TODO: file to be reverted back and changes to be moved to dashboard feature const HomePage: React.FC = () => { - console.log(window.location.href); // Logic to be moved ================================ const [visible, setVisible] = useState(false); const onClick = () => { From b7b1f7665ec60d368c7600214606b3354d22e131 Mon Sep 17 00:00:00 2001 From: akash744 Date: Wed, 9 Mar 2022 17:35:49 +1300 Subject: [PATCH 03/26] testing adding component --- .../src/components/HouseConflictModal.tsx | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 packages/frontend/src/components/HouseConflictModal.tsx diff --git a/packages/frontend/src/components/HouseConflictModal.tsx b/packages/frontend/src/components/HouseConflictModal.tsx new file mode 100644 index 00000000..c1c1af2e --- /dev/null +++ b/packages/frontend/src/components/HouseConflictModal.tsx @@ -0,0 +1,54 @@ +import { + Button, + Modal, + Text, + } from '@nextui-org/react'; + import React from 'react'; + + interface HouseConflictModalProps { + visible: boolean; + setVisible(value: boolean): void; + } + + const HouseConflictModal: React.FC = ({ + visible, + setVisible, + }) => { + + // ============================================== + + const closeHandler = () => { + setVisible(false); + }; + + return ( +
+ + + + + Already assigned to a house! + + + + + Please leave current house before joining a new house. + + + + + + +
+ ); + }; + + export default HouseConflictModal; + \ No newline at end of file From dc11bcb2233e4f9c39b58bd8197a9d54f51acd55 Mon Sep 17 00:00:00 2001 From: akash744 Date: Wed, 9 Mar 2022 20:59:02 +1300 Subject: [PATCH 04/26] logic for sharing code --- packages/frontend/src/pages/public/HomePage.tsx | 12 ++++++++++-- .../src/pages/routes/AuthenticatedRoutes.tsx | 2 +- packages/frontend/src/pages/routes/Router.tsx | 15 ++++++++++++++- .../src/pages/routes/UnauthenticatedRoutes.tsx | 9 +++++++-- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/pages/public/HomePage.tsx b/packages/frontend/src/pages/public/HomePage.tsx index 2fcfccd6..09a6e183 100644 --- a/packages/frontend/src/pages/public/HomePage.tsx +++ b/packages/frontend/src/pages/public/HomePage.tsx @@ -4,16 +4,23 @@ import { useApi } from '../../hooks/useApi'; import { useAuth } from '../../hooks/useAuth'; import ShareLinkModal from '../../components/ShareLinkModal'; import { LinkIcon } from '@heroicons/react/outline'; +import HouseConflictModal from '../../components/HouseConflictModal'; -interface HomePageProps {} +interface HomePageProps { + alreadyInFlat: boolean +} // TODO: file to be reverted back and changes to be moved to dashboard feature -const HomePage: React.FC = () => { +const HomePage: React.FC = ({alreadyInFlat}: HomePageProps) => { + + console.log(alreadyInFlat); // Logic to be moved ================================ const [visible, setVisible] = useState(false); const onClick = () => { setVisible(true); }; + + const [conflictVisible, conflictsetVisible] = useState(alreadyInFlat); // ================================================== const { data } = useApi('/api/v1/ping', { @@ -28,6 +35,7 @@ const HomePage: React.FC = () => { return (
+ Hello

{user?.displayName}

diff --git a/packages/frontend/src/pages/routes/AuthenticatedRoutes.tsx b/packages/frontend/src/pages/routes/AuthenticatedRoutes.tsx index 232b5a8e..ec5e8bf8 100644 --- a/packages/frontend/src/pages/routes/AuthenticatedRoutes.tsx +++ b/packages/frontend/src/pages/routes/AuthenticatedRoutes.tsx @@ -8,7 +8,7 @@ interface AuthenticatedRoutesProps {} const AuthenticatedRoutes: React.FC = () => { return ( - } /> + } /> } /> ); diff --git a/packages/frontend/src/pages/routes/Router.tsx b/packages/frontend/src/pages/routes/Router.tsx index f8f2d8e2..18026b29 100644 --- a/packages/frontend/src/pages/routes/Router.tsx +++ b/packages/frontend/src/pages/routes/Router.tsx @@ -3,12 +3,20 @@ import AuthenticatedRoutes from './AuthenticatedRoutes'; import UnauthenticatedRoutes from './UnauthenticatedRoutes'; import { getAuth } from 'firebase/auth'; import { useAuth } from '../../hooks/useAuth'; +import { useLocation } from 'react-router-dom'; interface RouterProps {} const Router: React.FC = () => { const { authLoaded, setAuthLoaded, signedIn, setUser } = useAuth(); + const location = useLocation() + // change to api call + const getUserHouseCode = () => { + return { data: '123456' }; + }; + const { data: code } = getUserHouseCode(); + useEffect(() => { // returns function to stop the listener const clearListener = getAuth().onAuthStateChanged((user) => { @@ -25,7 +33,12 @@ const Router: React.FC = () => { } if (!signedIn) { - return ; + let path = location.pathname + // To Do: Add to House + if (code === path.slice(path.indexOf("=") + 1)){ + return ; + } + return ; } return ; diff --git a/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx b/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx index b3a338f1..57a9ca5a 100644 --- a/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx +++ b/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx @@ -3,14 +3,19 @@ import { Routes, Route, Navigate } from 'react-router'; import LandingPage from '../public/LandingPage'; import SignInPage from '../public/SignInPage'; +import HomePage from '../public/HomePage'; -interface UnauthenticatedRoutesProps {} +interface UnauthenticatedRoutesProps { + alreadyInFlat: boolean +} -const UnauthenticatedRoutes: React.FC = () => { +const UnauthenticatedRoutes: React.FC = ({alreadyInFlat}: UnauthenticatedRoutesProps) => { return ( } /> } /> + } /> + } /> } /> ); From 8986521f438fd24d7fa6556c193bddd3dabee62b Mon Sep 17 00:00:00 2001 From: akash744 Date: Wed, 9 Mar 2022 21:21:15 +1300 Subject: [PATCH 05/26] reminder to change expression --- packages/frontend/src/pages/routes/Router.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frontend/src/pages/routes/Router.tsx b/packages/frontend/src/pages/routes/Router.tsx index 18026b29..1a5343e6 100644 --- a/packages/frontend/src/pages/routes/Router.tsx +++ b/packages/frontend/src/pages/routes/Router.tsx @@ -35,6 +35,7 @@ const Router: React.FC = () => { if (!signedIn) { let path = location.pathname // To Do: Add to House + // To-do: Check if user house object is empty if (code === path.slice(path.indexOf("=") + 1)){ return ; } From 3457baade48dbb1a87f8395ff4bf9becfa7decbb Mon Sep 17 00:00:00 2001 From: alyu201 Date: Wed, 9 Mar 2022 23:08:31 +1300 Subject: [PATCH 06/26] Redirect user to sign in page for shared link --- .../frontend/src/pages/public/HomePage.tsx | 33 +++++++++++-------- packages/frontend/src/pages/routes/Router.tsx | 21 +++++++----- .../pages/routes/UnauthenticatedRoutes.tsx | 9 +++-- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/packages/frontend/src/pages/public/HomePage.tsx b/packages/frontend/src/pages/public/HomePage.tsx index 09a6e183..f013a14f 100644 --- a/packages/frontend/src/pages/public/HomePage.tsx +++ b/packages/frontend/src/pages/public/HomePage.tsx @@ -1,5 +1,5 @@ import { Button, Spacer, Text } from '@nextui-org/react'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { useApi } from '../../hooks/useApi'; import { useAuth } from '../../hooks/useAuth'; import ShareLinkModal from '../../components/ShareLinkModal'; @@ -7,21 +7,29 @@ import { LinkIcon } from '@heroicons/react/outline'; import HouseConflictModal from '../../components/HouseConflictModal'; interface HomePageProps { - alreadyInFlat: boolean + alreadyInFlat: boolean; } -// TODO: file to be reverted back and changes to be moved to dashboard feature -const HomePage: React.FC = ({alreadyInFlat}: HomePageProps) => { - - console.log(alreadyInFlat); - // Logic to be moved ================================ +const HomePage: React.FC = ({ + alreadyInFlat, +}: HomePageProps) => { + const { user } = useAuth(); const [visible, setVisible] = useState(false); const onClick = () => { setVisible(true); }; + // TODO: call api endpoint + const joinHouse = () => { + if (localStorage.getItem('code')) { + console.log('user added to the house'); + } + }; + useEffect(() => { + joinHouse(); + }, []); + const [conflictVisible, conflictsetVisible] = useState(alreadyInFlat); - // ================================================== const { data } = useApi('/api/v1/ping', { method: 'get', @@ -31,15 +39,15 @@ const HomePage: React.FC = ({alreadyInFlat}: HomePageProps) => { console.log({ env, time }); } - const { user } = useAuth(); - return (
- + Hello

{user?.displayName}

- {/* Logic to be moved ==========================*/} - {/* ============================================*/}
); }; diff --git a/packages/frontend/src/pages/routes/Router.tsx b/packages/frontend/src/pages/routes/Router.tsx index 18026b29..46c26478 100644 --- a/packages/frontend/src/pages/routes/Router.tsx +++ b/packages/frontend/src/pages/routes/Router.tsx @@ -1,17 +1,18 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import AuthenticatedRoutes from './AuthenticatedRoutes'; import UnauthenticatedRoutes from './UnauthenticatedRoutes'; import { getAuth } from 'firebase/auth'; import { useAuth } from '../../hooks/useAuth'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useSearchParams } from 'react-router-dom'; interface RouterProps {} const Router: React.FC = () => { const { authLoaded, setAuthLoaded, signedIn, setUser } = useAuth(); + const [searchParams] = useSearchParams(); + const inviteCode = searchParams.get('join') ?? ''; - const location = useLocation() - // change to api call + // change to api call const getUserHouseCode = () => { return { data: '123456' }; }; @@ -33,12 +34,14 @@ const Router: React.FC = () => { } if (!signedIn) { - let path = location.pathname + // Route to sign-in page with stored code + localStorage.setItem('code', inviteCode); + // To Do: Add to House - if (code === path.slice(path.indexOf("=") + 1)){ - return ; - } - return ; + // if (code === inviteCode) { + // return ; + // } + return ; } return ; diff --git a/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx b/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx index 57a9ca5a..38b5e3fc 100644 --- a/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx +++ b/packages/frontend/src/pages/routes/UnauthenticatedRoutes.tsx @@ -6,16 +6,19 @@ import SignInPage from '../public/SignInPage'; import HomePage from '../public/HomePage'; interface UnauthenticatedRoutesProps { - alreadyInFlat: boolean + alreadyInFlat: boolean; } -const UnauthenticatedRoutes: React.FC = ({alreadyInFlat}: UnauthenticatedRoutesProps) => { +const UnauthenticatedRoutes: React.FC = ({ + alreadyInFlat, +}: UnauthenticatedRoutesProps) => { return ( } /> } /> } /> - } /> + } /> + {/* } /> */} } /> ); From 02fa0eac520afd15852de88957924d61349fafa2 Mon Sep 17 00:00:00 2001 From: alyu201 Date: Sun, 13 Mar 2022 23:19:02 +1300 Subject: [PATCH 07/26] Removed .vscode file --- .vscode/rtse.code-snippets | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .vscode/rtse.code-snippets diff --git a/.vscode/rtse.code-snippets b/.vscode/rtse.code-snippets deleted file mode 100644 index 0dcaaf76..00000000 --- a/.vscode/rtse.code-snippets +++ /dev/null @@ -1,33 +0,0 @@ -{ - // Place your platform workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and - // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope - // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is - // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: - // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. - // Placeholders with the same ids are connected. - // Example: - // "Print to console": { - // "scope": "javascript,typescript", - // "prefix": "log", - // "body": [ - // "console.log('$1');", - // "$2" - // ], - // "description": "Log output to console" - // } - "Create a typescript default export functional react component": { - "prefix": "rtse", - "body": [ - "import React from \"react\"", - "", - "interface ${1:$TM_FILENAME_BASE}Props {}", - "", - "const ${1:$TM_FILENAME_BASE}: React.FC<${1:$TM_FILENAME_BASE}Props> = () => {", - " $0", - " return
", - "}", - "", - "export default $TM_FILENAME_BASE" - ] - } -} From d52695fedc26bf7d807ff761b07b0c5fb8f4f599 Mon Sep 17 00:00:00 2001 From: alyu201 Date: Mon, 14 Mar 2022 11:00:57 +1300 Subject: [PATCH 08/26] Cypress initial setup --- .vscode/rtse.code-snippets | 33 + packages/frontend/cypress.json | 1 + .../frontend/cypress/fixtures/example.json | 5 + .../1-getting-started/todo.spec.js | 143 ++++ .../2-advanced-examples/actions.spec.js | 315 ++++++++ .../2-advanced-examples/aliasing.spec.js | 43 ++ .../2-advanced-examples/assertions.spec.js | 176 +++++ .../2-advanced-examples/connectors.spec.js | 96 +++ .../2-advanced-examples/cookies.spec.js | 79 ++ .../2-advanced-examples/cypress_api.spec.js | 215 ++++++ .../2-advanced-examples/files.spec.js | 90 +++ .../2-advanced-examples/local_storage.spec.js | 58 ++ .../2-advanced-examples/location.spec.js | 34 + .../2-advanced-examples/misc.spec.js | 102 +++ .../2-advanced-examples/navigation.spec.js | 56 ++ .../network_requests.spec.js | 184 +++++ .../2-advanced-examples/querying.spec.js | 106 +++ .../spies_stubs_clocks.spec.js | 217 ++++++ .../2-advanced-examples/traversal.spec.js | 116 +++ .../2-advanced-examples/utilities.spec.js | 111 +++ .../2-advanced-examples/viewport.spec.js | 59 ++ .../2-advanced-examples/waiting.spec.js | 33 + .../2-advanced-examples/window.spec.js | 22 + .../cypress/integration/sample_spec.js | 6 + packages/frontend/cypress/plugins/index.js | 22 + packages/frontend/cypress/support/commands.js | 25 + packages/frontend/cypress/support/index.js | 20 + packages/frontend/package.json | 1 + packages/frontend/yarn.lock | 676 +++++++++++++++++- 29 files changed, 3025 insertions(+), 19 deletions(-) create mode 100644 .vscode/rtse.code-snippets create mode 100644 packages/frontend/cypress.json create mode 100644 packages/frontend/cypress/fixtures/example.json create mode 100644 packages/frontend/cypress/integration/1-getting-started/todo.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/actions.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/aliasing.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/assertions.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/connectors.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/cookies.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/cypress_api.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/files.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/local_storage.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/location.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/misc.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/navigation.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/network_requests.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/querying.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/spies_stubs_clocks.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/traversal.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/utilities.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/viewport.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/waiting.spec.js create mode 100644 packages/frontend/cypress/integration/2-advanced-examples/window.spec.js create mode 100644 packages/frontend/cypress/integration/sample_spec.js create mode 100644 packages/frontend/cypress/plugins/index.js create mode 100644 packages/frontend/cypress/support/commands.js create mode 100644 packages/frontend/cypress/support/index.js diff --git a/.vscode/rtse.code-snippets b/.vscode/rtse.code-snippets new file mode 100644 index 00000000..0dcaaf76 --- /dev/null +++ b/.vscode/rtse.code-snippets @@ -0,0 +1,33 @@ +{ + // Place your platform workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and + // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope + // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is + // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: + // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. + // Placeholders with the same ids are connected. + // Example: + // "Print to console": { + // "scope": "javascript,typescript", + // "prefix": "log", + // "body": [ + // "console.log('$1');", + // "$2" + // ], + // "description": "Log output to console" + // } + "Create a typescript default export functional react component": { + "prefix": "rtse", + "body": [ + "import React from \"react\"", + "", + "interface ${1:$TM_FILENAME_BASE}Props {}", + "", + "const ${1:$TM_FILENAME_BASE}: React.FC<${1:$TM_FILENAME_BASE}Props> = () => {", + " $0", + " return
", + "}", + "", + "export default $TM_FILENAME_BASE" + ] + } +} diff --git a/packages/frontend/cypress.json b/packages/frontend/cypress.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/frontend/cypress.json @@ -0,0 +1 @@ +{} diff --git a/packages/frontend/cypress/fixtures/example.json b/packages/frontend/cypress/fixtures/example.json new file mode 100644 index 00000000..02e42543 --- /dev/null +++ b/packages/frontend/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} diff --git a/packages/frontend/cypress/integration/1-getting-started/todo.spec.js b/packages/frontend/cypress/integration/1-getting-started/todo.spec.js new file mode 100644 index 00000000..39d1dbe5 --- /dev/null +++ b/packages/frontend/cypress/integration/1-getting-started/todo.spec.js @@ -0,0 +1,143 @@ +/// + +// Welcome to Cypress! +// +// This spec file contains a variety of sample tests +// for a todo list app that are designed to demonstrate +// the power of writing tests in Cypress. +// +// To learn more about how Cypress works and +// what makes it such an awesome testing tool, +// please read our getting started guide: +// https://on.cypress.io/introduction-to-cypress + +describe('example to-do app', () => { + beforeEach(() => { + // Cypress starts out with a blank slate for each test + // so we must tell it to visit our website with the `cy.visit()` command. + // Since we want to visit the same URL at the start of all our tests, + // we include it in our beforeEach function so that it runs before each test + cy.visit('https://example.cypress.io/todo'); + }); + + it('displays two todo items by default', () => { + // We use the `cy.get()` command to get all elements that match the selector. + // Then, we use `should` to assert that there are two matched items, + // which are the two default items. + cy.get('.todo-list li').should('have.length', 2); + + // We can go even further and check that the default todos each contain + // the correct text. We use the `first` and `last` functions + // to get just the first and last matched elements individually, + // and then perform an assertion with `should`. + cy.get('.todo-list li').first().should('have.text', 'Pay electric bill'); + cy.get('.todo-list li').last().should('have.text', 'Walk the dog'); + }); + + it('can add new todo items', () => { + // We'll store our item text in a variable so we can reuse it + const newItem = 'Feed the cat'; + + // Let's get the input element and use the `type` command to + // input our new list item. After typing the content of our item, + // we need to type the enter key as well in order to submit the input. + // This input has a data-test attribute so we'll use that to select the + // element in accordance with best practices: + // https://on.cypress.io/selecting-elements + cy.get('[data-test=new-todo]').type(`${newItem}{enter}`); + + // Now that we've typed our new item, let's check that it actually was added to the list. + // Since it's the newest item, it should exist as the last element in the list. + // In addition, with the two default items, we should have a total of 3 elements in the list. + // Since assertions yield the element that was asserted on, + // we can chain both of these assertions together into a single statement. + cy.get('.todo-list li') + .should('have.length', 3) + .last() + .should('have.text', newItem); + }); + + it('can check off an item as completed', () => { + // In addition to using the `get` command to get an element by selector, + // we can also use the `contains` command to get an element by its contents. + // However, this will yield the