test fixup

This commit is contained in:
Jake McDermott
2019-01-02 02:28:24 -05:00
parent 8f4437e17e
commit 31d0347553
7 changed files with 108 additions and 141 deletions

View File

@@ -1,38 +0,0 @@
import * as endpoints from '../src/endpoints';
const axios = require('axios');
const mockAPIConfigData = {
data: {
custom_virtualenvs: ['foo', 'bar'],
ansible_version: "2.7.2",
version: "2.1.1-40-g2758a3848"
}
};
jest.genMockFromModule('axios');
axios.create = jest.fn(() => axios);
axios.get = jest.fn(() => axios);
axios.post = jest.fn(() => axios);
axios.create.mockReturnValue({
get: axios.get,
post: axios.post
});
axios.get.mockImplementation((endpoint) => {
if (endpoint === endpoints.API_CONFIG) {
return new Promise((resolve, reject) => {
resolve(mockAPIConfigData);
});
}
else {
return 'get results';
}
});
axios.post.mockResolvedValue('post results');
axios.customClearMocks = () => {
axios.create.mockClear();
axios.get.mockClear();
axios.post.mockClear();
};
module.exports = axios;

View File

@@ -4,7 +4,7 @@ import { HashRouter as Router } from 'react-router-dom';
import { shallow, mount } from 'enzyme'; import { shallow, mount } from 'enzyme';
import App from '../src/App'; import App from '../src/App';
import api from '../src/api'; import api from '../src/api';
import { API_LOGOUT, API_CONFIG } from '../src/endpoints'; import { API_LOGOUT } from '../src/endpoints';
import Dashboard from '../src/pages/Dashboard'; import Dashboard from '../src/pages/Dashboard';
import { asyncFlush } from '../jest.setup'; import { asyncFlush } from '../jest.setup';
@@ -45,6 +45,7 @@ describe('<App />', () => {
const appWrapper = shallow(<App />); const appWrapper = shallow(<App />);
appWrapper.instance().onDevLogout(); appWrapper.instance().onDevLogout();
appWrapper.setState({ activeGroup: 'foo', activeItem: 'bar' }); appWrapper.setState({ activeGroup: 'foo', activeItem: 'bar' });
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(API_LOGOUT); expect(api.get).toHaveBeenCalledWith(API_LOGOUT);
await asyncFlush(); await asyncFlush();
expect(appWrapper.state().activeItem).toBe(DEFAULT_ACTIVE_ITEM); expect(appWrapper.state().activeItem).toBe(DEFAULT_ACTIVE_ITEM);

View File

@@ -1,80 +1,61 @@
import mockAxios from 'axios';
import APIClient from '../src/api'; import APIClient from '../src/api';
import * as endpoints from '../src/endpoints';
const CSRF_COOKIE_NAME = 'csrftoken'; const invalidCookie = 'invalid';
const CSRF_HEADER_NAME = 'X-CSRFToken'; const validLoggedOutCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=false; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge';
const validLoggedInCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=true; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge';
const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
describe('APIClient (api.js)', () => { describe('APIClient (api.js)', () => {
afterEach(() => { test('isAuthenticated returns false when cookie is invalid', () => {
mockAxios.customClearMocks(); APIClient.getCookie = jest.fn(() => invalidCookie);
const api = new APIClient();
expect(api.isAuthenticated()).toBe(false);
}); });
test('constructor calls axios create', () => { test('isAuthenticated returns false when cookie is unauthenticated', () => {
const csrfObj = { APIClient.getCookie = jest.fn(() => validLoggedOutCookie);
xsrfCookieName: CSRF_COOKIE_NAME,
xsrfHeaderName: CSRF_HEADER_NAME const api = new APIClient();
}; expect(api.isAuthenticated()).toBe(false);
expect(mockAxios.create).toHaveBeenCalledTimes(1);
expect(mockAxios.create).toHaveBeenCalledWith(csrfObj);
expect(APIClient.http).toHaveProperty('get');
}); });
test('isAuthenticated checks authentication and sets cookie from document', () => { test('isAuthenticated returns true when cookie is valid and authenticated', () => {
APIClient.getCookie = jest.fn(); APIClient.getCookie = jest.fn(() => validLoggedInCookie);
const invalidCookie = 'invalid';
const validLoggedOutCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=false; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge'; const api = new APIClient();
const validLoggedInCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=true; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge'; expect(api.isAuthenticated()).toBe(true);
APIClient.getCookie.mockReturnValue(invalidCookie);
expect(APIClient.isAuthenticated()).toBe(false);
APIClient.getCookie.mockReturnValue(validLoggedOutCookie);
expect(APIClient.isAuthenticated()).toBe(false);
APIClient.getCookie.mockReturnValue(validLoggedInCookie);
expect(APIClient.isAuthenticated()).toBe(true);
}); });
test('login calls get and post to login route, and sets cookie from document', (done) => { test('login calls get and post with expected content headers', async (done) => {
const un = 'foo'; const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
const pw = 'bar';
const next = 'baz'; const createPromise = () => Promise.resolve();
const headers = { 'Content-Type': LOGIN_CONTENT_TYPE }; const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) });
const data = `username=${un}&password=${pw}&next=${next}`;
APIClient.setCookie = jest.fn(); const api = new APIClient(mockHttp);
APIClient.login(un, pw, next).then(() => { await api.login('username', 'password');
expect(mockAxios.get).toHaveBeenCalledTimes(1);
expect(mockAxios.get).toHaveBeenCalledWith(endpoints.API_LOGIN, { headers }); expect(mockHttp.get).toHaveBeenCalledTimes(1);
expect(mockAxios.post).toHaveBeenCalledTimes(1); expect(mockHttp.get.mock.calls[0]).toContainEqual({ headers });
expect(mockAxios.post).toHaveBeenCalledWith(endpoints.API_LOGIN, data, { headers });
done(); expect(mockHttp.post).toHaveBeenCalledTimes(1);
}); expect(mockHttp.post.mock.calls[0]).toContainEqual({ headers });
done();
}); });
test('login encodes uri components for username, password and redirect', (done) => { test('login sends expected data', async (done) => {
const un = '/foo/'; const createPromise = () => Promise.resolve();
const pw = '/bar/'; const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) });
const next = '/baz/';
const headers = { 'Content-Type': LOGIN_CONTENT_TYPE };
const data = `username=${encodeURIComponent(un)}&password=${encodeURIComponent(pw)}&next=${encodeURIComponent(next)}`;
APIClient.login(un, pw, next).then(() => {
expect(mockAxios.post).toHaveBeenCalledTimes(1);
expect(mockAxios.post).toHaveBeenCalledWith(endpoints.API_LOGIN, data, { headers });
done();
});
});
test('login redirect defaults to config route when not explicitly passed', (done) => { const api = new APIClient(mockHttp);
const un = 'foo'; await api.login('foo', 'bar');
const pw = 'bar'; await api.login('foo', 'bar', 'baz');
const headers = { 'Content-Type': LOGIN_CONTENT_TYPE };
const data = `username=${un}&password=${pw}&next=${encodeURIComponent(endpoints.API_CONFIG)}`;
APIClient.setCookie = jest.fn();
APIClient.login(un, pw).then(() => {
expect(mockAxios.post).toHaveBeenCalledTimes(1);
expect(mockAxios.post).toHaveBeenCalledWith(endpoints.API_LOGIN, data, { headers });
done();
});
});
expect(mockHttp.post).toHaveBeenCalledTimes(2);
expect(mockHttp.post.mock.calls[0]).toContainEqual('username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F');
expect(mockHttp.post.mock.calls[1]).toContainEqual('username=foo&password=bar&next=baz');
done();
});
}); });

View File

@@ -1,22 +1,38 @@
import React from 'react'; import { mount } from 'enzyme';
import ReactDOM from 'react-dom';
import api from '../src/api';
import { main } from '../src/index'; import { main } from '../src/index';
const custom_logo = (<div>logo</div>); const render = template => mount(template);
const custom_login_info = 'custom login info'; const data = { custom_logo: 'foo', custom_login_info: '' }
jest.mock('react-dom', () => ({ render: jest.fn() }));
describe('index.jsx', () => { describe('index.jsx', () => {
test('renders without crashing', async () => { test('initialization', async (done) => {
api.getRoot = jest.fn().mockImplementation(() => Promise const isAuthenticated = () => false;
.resolve({ data: { custom_logo, custom_login_info } })); const getRoot = jest.fn(() => Promise.resolve({ data }));
await main(); const api = { getRoot, isAuthenticated };
const wrapper = await main(render, api);
expect(ReactDOM.render).toHaveBeenCalled(); expect(api.getRoot).toHaveBeenCalled();
expect(wrapper.find('Dashboard')).toHaveLength(0);
expect(wrapper.find('Login')).toHaveLength(1);
const { src } = wrapper.find('Login Brand img').props();
expect(src).toContain(data.custom_logo);
done();
});
test('dashboard is loaded when authenticated', async (done) => {
const isAuthenticated = () => true;
const getRoot = jest.fn(() => Promise.resolve({ data }));
const api = { getRoot, isAuthenticated };
const wrapper = await main(render, api);
expect(api.getRoot).toHaveBeenCalled();
expect(wrapper.find('Dashboard')).toHaveLength(1);
expect(wrapper.find('Login')).toHaveLength(0);
done();
}); });
}); });

View File

@@ -4,7 +4,7 @@ import { mount, shallow } from 'enzyme';
import { I18nProvider } from '@lingui/react'; import { I18nProvider } from '@lingui/react';
import { asyncFlush } from '../../jest.setup'; import { asyncFlush } from '../../jest.setup';
import AtLogin from '../../src/pages/Login'; import AtLogin from '../../src/pages/Login';
import api from '../../src/api'; import APIClient from '../../src/api';
describe('<Login />', () => { describe('<Login />', () => {
let loginWrapper; let loginWrapper;
@@ -16,6 +16,8 @@ describe('<Login />', () => {
let submitButton; let submitButton;
let loginHeaderLogo; let loginHeaderLogo;
const api = new APIClient({});
const findChildren = () => { const findChildren = () => {
atLogin = loginWrapper.find('AtLogin'); atLogin = loginWrapper.find('AtLogin');
loginPage = loginWrapper.find('LoginPage'); loginPage = loginWrapper.find('LoginPage');
@@ -30,7 +32,7 @@ describe('<Login />', () => {
loginWrapper = mount( loginWrapper = mount(
<MemoryRouter> <MemoryRouter>
<I18nProvider> <I18nProvider>
<AtLogin /> <AtLogin api={api} />
</I18nProvider> </I18nProvider>
</MemoryRouter> </MemoryRouter>
); );
@@ -59,7 +61,7 @@ describe('<Login />', () => {
loginWrapper = mount( loginWrapper = mount(
<MemoryRouter> <MemoryRouter>
<I18nProvider> <I18nProvider>
<AtLogin logo="images/foo.jpg" alt="Foo Application" /> <AtLogin api={api} logo="images/foo.jpg" alt="Foo Application" />
</I18nProvider> </I18nProvider>
</MemoryRouter> </MemoryRouter>
); );
@@ -73,7 +75,7 @@ describe('<Login />', () => {
loginWrapper = mount( loginWrapper = mount(
<MemoryRouter> <MemoryRouter>
<I18nProvider> <I18nProvider>
<AtLogin /> <AtLogin api={api} />
</I18nProvider> </I18nProvider>
</MemoryRouter> </MemoryRouter>
); );
@@ -166,7 +168,7 @@ describe('<Login />', () => {
test('render Redirect to / when already authenticated', () => { test('render Redirect to / when already authenticated', () => {
api.isAuthenticated = jest.fn(); api.isAuthenticated = jest.fn();
api.isAuthenticated.mockReturnValue(true); api.isAuthenticated.mockReturnValue(true);
loginWrapper = shallow(<AtLogin />); loginWrapper = shallow(<AtLogin api={api} />);
const redirectElem = loginWrapper.find('Redirect'); const redirectElem = loginWrapper.find('Redirect');
expect(redirectElem.length).toBe(1); expect(redirectElem.length).toBe(1);
expect(redirectElem.props().to).toBe('/'); expect(redirectElem.props().to).toBe('/');

View File

@@ -1,5 +1,3 @@
import axios from 'axios';
const API_ROOT = '/api/'; const API_ROOT = '/api/';
const API_LOGIN = `${API_ROOT}login/`; const API_LOGIN = `${API_ROOT}login/`;
const API_LOGOUT = `${API_ROOT}logout/`; const API_LOGOUT = `${API_ROOT}logout/`;
@@ -7,21 +5,14 @@ const API_V2 = `${API_ROOT}v2/`;
const API_CONFIG = `${API_V2}config/`; const API_CONFIG = `${API_V2}config/`;
const API_ORGANIZATIONS = `${API_V2}organizations/`; const API_ORGANIZATIONS = `${API_V2}organizations/`;
const CSRF_COOKIE_NAME = 'csrftoken';
const CSRF_HEADER_NAME = 'X-CSRFToken';
const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded'; const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
const defaultHttpAdapter = axios.create({
xsrfCookieName: CSRF_COOKIE_NAME,
xsrfHeaderName: CSRF_HEADER_NAME,
});
class APIClient { class APIClient {
static getCookie () { static getCookie () {
return document.cookie; return document.cookie;
} }
constructor (httpAdapter = defaultHttpAdapter) { constructor (httpAdapter) {
this.http = httpAdapter; this.http = httpAdapter;
} }

View File

@@ -1,5 +1,6 @@
import axios from 'axios';
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import ReactDOM from 'react-dom';
import { import {
HashRouter, HashRouter,
Redirect, Redirect,
@@ -48,15 +49,28 @@ import Users from './pages/Users';
import ja from '../build/locales/ja/messages'; import ja from '../build/locales/ja/messages';
import en from '../build/locales/en/messages'; import en from '../build/locales/en/messages';
const catalogs = { en, ja }; //
// Initialize http
//
const http = axios.create({ xsrfCookieName: 'csrftoken', xsrfHeaderName: 'X-CSRFToken' });
//
// Derive the language and region from global user agent data. Example: es-US // Derive the language and region from global user agent data. Example: es-US
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator // see: https://developer.mozilla.org/en-US/docs/Web/API/Navigator
//
const language = (navigator.languages && navigator.languages[0]) const language = (navigator.languages && navigator.languages[0])
|| navigator.language || navigator.language
|| navigator.userLanguage; || navigator.userLanguage;
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0]; const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];
const catalogs = { en, ja };
export async function main (api) { //
// Function Main
//
export async function main (render, api) {
const el = document.getElementById('app'); const el = document.getElementById('app');
// fetch additional config from server // fetch additional config from server
const { data } = await api.getRoot(); const { data } = await api.getRoot();
@@ -78,7 +92,7 @@ export async function main (api) {
</Switch> </Switch>
); );
render( return render(
<HashRouter> <HashRouter>
<I18nProvider <I18nProvider
language={languageWithoutRegionCode} language={languageWithoutRegionCode}
@@ -89,8 +103,8 @@ export async function main (api) {
<Background> <Background>
{!api.isAuthenticated() ? loginRoutes : ( {!api.isAuthenticated() ? loginRoutes : (
<Switch> <Switch>
<Route path="/login" render={() => <Redirect to="/home" />} /> <Route path="/login" render={() => (<Redirect to="/home" />)} />
<Route exact path="/" render={() => <Redirect to="/home" />} /> <Route exact path="/" render={() => (<Redirect to="/home" />)} />
<Route <Route
render={() => ( render={() => (
<App <App
@@ -266,4 +280,4 @@ export async function main (api) {
</HashRouter>, el); </HashRouter>, el);
}; };
export default main(new APIClient()); main(ReactDOM.render, new APIClient(http));