From 60e93a84973fc9003c583c94268698f9464a819d Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Wed, 28 Oct 2020 00:15:30 +0100
Subject: [PATCH] Add ui tests for routes

---
 ui/.eslintrc.js                               | 46 +++++++++++++++++++
 ui/.eslintrc.json                             | 28 -----------
 ui/src/components/routes/AuthorizedRoute.js   |  6 +--
 .../components/routes/AuthorizedRoute.test.js | 38 +++++++++++++++
 ui/src/components/routes/Routes.test.js       | 41 +++++++++++++++++
 5 files changed, 128 insertions(+), 31 deletions(-)
 create mode 100644 ui/.eslintrc.js
 delete mode 100644 ui/.eslintrc.json
 create mode 100644 ui/src/components/routes/AuthorizedRoute.test.js
 create mode 100644 ui/src/components/routes/Routes.test.js

diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
new file mode 100644
index 0000000..602967f
--- /dev/null
+++ b/ui/.eslintrc.js
@@ -0,0 +1,46 @@
+module.exports = {
+  env: {
+    browser: true,
+    es6: true,
+  },
+  extends: ['eslint:recommended', 'plugin:react/recommended'],
+  globals: {
+    Atomics: 'readonly',
+    SharedArrayBuffer: 'readonly',
+    process: 'readonly',
+    module: 'readonly',
+    require: 'readonly',
+  },
+  parserOptions: {
+    ecmaFeatures: {
+      jsx: true,
+    },
+    ecmaVersion: 2018,
+    sourceType: 'module',
+  },
+  plugins: ['react', 'react-hooks'],
+  rules: {
+    'no-unused-vars': 'warn',
+    'react/display-name': 'off',
+  },
+  settings: {
+    react: {
+      version: 'detect',
+    },
+  },
+  parser: 'babel-eslint',
+  overrides: [
+    Object.assign(require('eslint-plugin-jest').configs.recommended, {
+      files: ['**/*.test.js'],
+      env: { jest: true },
+      plugins: ['jest', 'jest-dom'],
+      rules: Object.assign(
+        require('eslint-plugin-jest').configs.recommended.rules,
+        {
+          'no-import-assign': 'off',
+          'react/prop-types': 'off',
+        }
+      ),
+    }),
+  ],
+}
diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json
deleted file mode 100644
index c97ff17..0000000
--- a/ui/.eslintrc.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "env": {
-    "browser": true,
-    "es6": true,
-    "jest/globals": true
-  },
-  "extends": ["eslint:recommended", "plugin:react/recommended"],
-  "globals": {
-    "Atomics": "readonly",
-    "SharedArrayBuffer": "readonly",
-    "process": "readonly",
-    "module": "readonly",
-    "require": "readonly"
-  },
-  "parserOptions": {
-    "ecmaFeatures": {
-      "jsx": true
-    },
-    "ecmaVersion": 2018,
-    "sourceType": "module"
-  },
-  "plugins": ["react", "react-hooks", "jest", "jest-dom"],
-  "rules": {
-    "no-unused-vars": "warn",
-    "react/display-name": "off"
-  },
-  "parser": "babel-eslint"
-}
diff --git a/ui/src/components/routes/AuthorizedRoute.js b/ui/src/components/routes/AuthorizedRoute.js
index 4b2d900..1eb55be 100644
--- a/ui/src/components/routes/AuthorizedRoute.js
+++ b/ui/src/components/routes/AuthorizedRoute.js
@@ -5,7 +5,7 @@ import { gql } from '@apollo/client'
 import { Query } from '@apollo/client/react/components'
 import { authToken } from '../../authentication'
 
-const adminQuery = gql`
+export const ADMIN_QUERY = gql`
   query adminQuery {
     myUser {
       admin
@@ -30,7 +30,7 @@ const AuthorizedRoute = ({ component: Component, admin = false, ...props }) => {
   let adminRedirect = null
   if (token && admin) {
     adminRedirect = (
-      <Query query={adminQuery}>
+      <Query query={ADMIN_QUERY}>
         {({ error, data }) => {
           if (error) alert(error)
 
@@ -59,7 +59,7 @@ const AuthorizedRoute = ({ component: Component, admin = false, ...props }) => {
 }
 
 AuthorizedRoute.propTypes = {
-  component: PropTypes.object.isRequired,
+  component: PropTypes.elementType.isRequired,
   admin: PropTypes.bool,
 }
 
diff --git a/ui/src/components/routes/AuthorizedRoute.test.js b/ui/src/components/routes/AuthorizedRoute.test.js
new file mode 100644
index 0000000..263be91
--- /dev/null
+++ b/ui/src/components/routes/AuthorizedRoute.test.js
@@ -0,0 +1,38 @@
+import '@testing-library/jest-dom'
+
+import React from 'react'
+import AuthorizedRoute from './AuthorizedRoute'
+import { render, screen } from '@testing-library/react'
+import { MemoryRouter, Route } from 'react-router-dom'
+
+import * as authentication from '../../authentication'
+
+describe('AuthorizedRoute component', () => {
+  const AuthorizedComponent = () => <div>authorized content</div>
+
+  test('not logged in', async () => {
+    authentication.authToken = jest.fn(() => null)
+
+    render(
+      <MemoryRouter initialEntries={['/']}>
+        <Route path="/login">login redirect</Route>
+        <AuthorizedRoute path="/" component={AuthorizedComponent} />
+      </MemoryRouter>
+    )
+
+    expect(screen.getByText('login redirect')).toBeInTheDocument()
+  })
+
+  test('logged in', async () => {
+    authentication.authToken = jest.fn(() => 'token-here')
+
+    render(
+      <MemoryRouter initialEntries={['/']}>
+        <Route path="/login">login redirect</Route>
+        <AuthorizedRoute path="/" component={AuthorizedComponent} />
+      </MemoryRouter>
+    )
+
+    expect(screen.getByText('authorized content')).toBeInTheDocument()
+  })
+})
diff --git a/ui/src/components/routes/Routes.test.js b/ui/src/components/routes/Routes.test.js
new file mode 100644
index 0000000..97dc92a
--- /dev/null
+++ b/ui/src/components/routes/Routes.test.js
@@ -0,0 +1,41 @@
+import '@testing-library/jest-dom'
+
+import React from 'react'
+
+import Routes from './Routes'
+import {
+  render,
+  screen,
+  waitForElementToBeRemoved,
+} from '@testing-library/react'
+import { MemoryRouter } from 'react-router-dom'
+
+describe('routes', () => {
+  test('unauthorized root path should navigate to login page', async () => {
+    jest.mock('../../Pages/LoginPage/LoginPage.js', () => () => (
+      <div>mocked login page</div>
+    ))
+
+    render(
+      <MemoryRouter initialEntries={['/']}>
+        <Routes />
+      </MemoryRouter>
+    )
+
+    await waitForElementToBeRemoved(() =>
+      screen.getByText('Loading', { exact: false })
+    )
+
+    expect(screen.getByText('mocked login page')).toBeInTheDocument()
+  })
+
+  test('invalid page should print a "not found" message', async () => {
+    render(
+      <MemoryRouter initialEntries={['/random_non_existent_page']}>
+        <Routes />
+      </MemoryRouter>
+    )
+
+    expect(screen.getByText('Page not found')).toBeInTheDocument()
+  })
+})
