8 Commits

Author SHA1 Message Date
Alidev123
ded4393d46 added unit tests 2020-08-14 17:06:37 -04:00
Alidev123
b3c9e32908 added unit tests 2020-08-14 17:05:13 -04:00
Alidev123
14e1dac68e added unit tests of Accounts, EditAccount, Profiles. ProfileDetails, AccessPoints, Alarms containers 2020-08-12 17:18:19 -04:00
Alidev123
670640f7d8 added unit tests fot AccessPointDetails container 2020-08-12 13:21:45 -04:00
Alidev123
8a2769b15f fixed warnings, added unit tests for Dashboard and AddProfile container 2020-08-11 16:52:55 -04:00
Alidev123
9dee5dae32 added unit tests for login container 2020-08-10 16:27:02 -04:00
Alidev123
8f63893190 Merge branch 'master' of https://github.com/Telecominfraproject/wlan-cloud-ui into feature/TW-986 2020-08-10 14:57:42 -04:00
Alidev123
8998b5ec0b added jest to eslint env 2020-08-10 14:57:18 -04:00
45 changed files with 20188 additions and 469 deletions

View File

@@ -1,6 +1,9 @@
{
"extends": ["airbnb", "prettier", "prettier/react"],
"plugins": ["prettier"],
"env": {
"jest": true
},
"rules": {
"react/jsx-filename-extension": [
1,

View File

@@ -1,74 +1,12 @@
import React, { useContext } from 'react';
import gql from 'graphql-tag';
import { useQuery, useMutation } from '@apollo/react-hooks';
import { Alert, notification } from 'antd';
import { Accounts as AccountsPage, Loading } from '@tip-wlan/wlan-cloud-ui-library';
import { GET_ALL_USERS } from 'graphql/queries';
import { CREATE_USER, UPDATE_USER, DELETE_USER } from 'graphql/mutations';
import UserContext from 'contexts/UserContext';
const GET_ALL_USERS = gql`
query GetAllUsers($customerId: ID!, $cursor: String) {
getAllUsers(customerId: $customerId, cursor: $cursor) {
items {
id
email: username
role
lastModifiedTimestamp
customerId
}
context {
cursor
lastPage
}
}
}
`;
const CREATE_USER = gql`
mutation CreateUser($username: String!, $password: String!, $role: String!, $customerId: ID!) {
createUser(username: $username, password: $password, role: $role, customerId: $customerId) {
username
role
customerId
}
}
`;
const UPDATE_USER = gql`
mutation UpdateUser(
$id: ID!
$username: String!
$password: String!
$role: String!
$customerId: ID!
$lastModifiedTimestamp: String
) {
updateUser(
id: $id
username: $username
password: $password
role: $role
customerId: $customerId
lastModifiedTimestamp: $lastModifiedTimestamp
) {
id
username
role
customerId
lastModifiedTimestamp
}
}
`;
const DELETE_USER = gql`
mutation DeleteUser($id: ID!) {
deleteUser(id: $id) {
id
}
}
`;
const Accounts = () => {
const { customerId } = useContext(UserContext);

View File

@@ -0,0 +1,222 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { fireEvent, render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { accountsMutationMock, accountsQueryMock } from './mock';
import Accounts from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<Accounts />', () => {
afterEach(jest.resetModules);
it('should render with data', async () => {
const { getByText } = render(
<MockedProvider mocks={[accountsQueryMock.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider mocks={[accountsQueryMock.error]} addTypename={false}>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load Users.')).toBeVisible());
});
it('deleting account should be successful with valid query', async () => {
const { getByText, getByRole, getAllByRole } = render(
<MockedProvider
mocks={[
accountsQueryMock.success,
accountsMutationMock.deleteAccountSuccess,
accountsQueryMock.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getAllByRole('button', { name: /delete/i })[0]);
expect(getByRole('button', { name: 'Delete' }));
fireEvent.click(getByRole('button', { name: 'Delete' }));
await waitFor(() => expect(getByText('Account successfully deleted.')).toBeVisible());
});
it('deleting account should not be successful with invalid query', async () => {
const { getByText, getByRole, getAllByRole } = render(
<MockedProvider
mocks={[accountsQueryMock.success, accountsMutationMock.deleteAccountError]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getAllByRole('button', { name: /delete/i })[0]);
expect(getByRole('button', { name: 'Delete' }));
fireEvent.click(getByRole('button', { name: 'Delete' }));
await waitFor(() => expect(getByText('Account could not be deleted.')).toBeVisible());
});
it('editing user should not be successful when query is invalid', async () => {
const { getByText, getByLabelText, getByRole, getAllByRole } = render(
<MockedProvider
mocks={[accountsQueryMock.success, accountsMutationMock.editUserError]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getAllByRole('button', { name: /edit/i })[0]);
expect(getByText('Edit Account')).toBeVisible();
fireEvent.change(getByLabelText('E-mail'), { target: { value: 'test@test.com' } });
fireEvent.change(getByLabelText('Password'), { target: { value: 'password' } });
fireEvent.change(getByLabelText('Confirm Password'), { target: { value: 'password' } });
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Account could not be updated.')).toBeVisible());
});
it('adding user should be successful when query is valid', async () => {
const { getByText, getByLabelText, getAllByRole, getByRole } = render(
<MockedProvider
mocks={[
accountsQueryMock.success,
accountsQueryMock.success,
accountsQueryMock.success,
accountsMutationMock.addUserSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getAllByRole('button', { name: /addaccount/i })[0]);
expect(getByText('Add Account', { selector: 'div' })).toBeVisible();
fireEvent.change(getByLabelText('E-mail'), { target: { value: 'test@test.com' } });
fireEvent.change(getByLabelText('Password'), { target: { value: 'password' } });
fireEvent.change(getByLabelText('Confirm Password'), { target: { value: 'password' } });
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Account successfully created.')).toBeVisible());
});
it('adding user should not be successful when query is invalid', async () => {
const { getByText, getByLabelText, getByRole } = render(
<MockedProvider
mocks={[accountsQueryMock.success, accountsMutationMock.addUserError]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getByRole('button', { name: /addaccount/i }));
expect(getByText('Add Account', { selector: 'div' })).toBeVisible();
fireEvent.change(getByLabelText('E-mail'), { target: { value: 'test@test.com' } });
fireEvent.change(getByLabelText('Password'), { target: { value: 'password' } });
fireEvent.change(getByLabelText('Confirm Password'), { target: { value: 'password' } });
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Account could not be created.')).toBeVisible());
});
it('fetchmore shoudl be called when onLoadMore button is clicked', async () => {
const { getByRole, getByText } = render(
<MockedProvider
mocks={[accountsQueryMock.success, accountsQueryMock.fetchMore, accountsQueryMock.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Load More' }));
await waitFor(() => expect(getByText('user-18')).toBeVisible());
});
it('editing user should be successful when query is valid', async () => {
const { getByText, getByLabelText, getByRole, getAllByRole } = render(
<MockedProvider
mocks={[
accountsQueryMock.success,
accountsQueryMock.success,
accountsMutationMock.editUserSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Accounts />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('user-0')).toBeVisible());
fireEvent.click(getAllByRole('button', { name: /edit/i })[0]);
await waitFor(() => expect(getByText('Edit Account')).toBeVisible());
fireEvent.change(getByLabelText('E-mail'), { target: { value: 'test@test.com' } });
fireEvent.change(getByLabelText('Password'), { target: { value: 'password' } });
fireEvent.change(getByLabelText('Confirm Password'), { target: { value: 'password' } });
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('user-0')).toBeVisible());
await waitFor(() => expect(getByText('Edit Account')).not.toBeVisible());
await waitFor(() => expect(getByText('Account successfully updated.')).toBeVisible());
}, 5000);
});

View File

@@ -0,0 +1,202 @@
import { GET_ALL_USERS } from 'graphql/queries';
import { CREATE_USER, UPDATE_USER, DELETE_USER } from 'graphql/mutations';
export const accountsQueryMock = {
success: {
request: {
query: GET_ALL_USERS,
variables: { customerId: 2 },
},
result: {
data: {
getAllUsers: {
items: [
{
id: '1',
email: 'user-0',
role: 'CustomerIT',
lastModifiedTimestamp: '1596814602673',
customerId: '2',
__typename: 'User',
},
{
id: '2',
email: 'user-1',
role: 'CustomerIT',
lastModifiedTimestamp: '1596814602814',
customerId: '2',
__typename: 'User',
},
{
id: '3',
email: 'user-2',
role: 'CustomerIT',
lastModifiedTimestamp: '1596814602815',
customerId: '2',
__typename: 'User',
},
{
id: '4',
email: 'user-3',
role: 'CustomerIT',
lastModifiedTimestamp: '1596814602815',
customerId: '2',
__typename: 'User',
},
{
id: '5',
email: 'user-4',
role: 'CustomerIT',
lastModifiedTimestamp: '1596814602815',
customerId: '2',
__typename: 'User',
},
],
context: {
cursor: 'test',
lastPage: false,
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
fetchMore: {
request: {
query: GET_ALL_USERS,
variables: { customerId: 2, cursor: 'test' },
},
result: {
data: {
getAllUsers: {
items: [
{
id: '18',
email: 'user-18',
role: 'CustomerIT',
lastModifiedTimestamp: '1596814602673',
customerId: '2',
__typename: 'User',
},
],
context: {
cursor: 'test',
lastPage: true,
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_ALL_USERS,
variables: { customerId: 2 },
errorPolicy: 'all',
},
result: {
data: null,
errors: [{}],
loading: false,
networkStatus: 7,
stale: false,
},
},
};
export const accountsMutationMock = {
addUserSuccess: {
request: {
query: CREATE_USER,
variables: {
username: 'test@test.com',
password: 'password',
role: 'CustomerIT',
customerId: 2,
},
},
result: {
data: {
createUser: {
username: 'test@test.com',
role: 'CustomerIT',
customerId: '2',
},
},
},
},
addUserError: {
request: {
query: CREATE_USER,
variables: {},
},
result: {
data: null,
errors: [{}],
},
},
editUserSuccess: {
request: {
query: UPDATE_USER,
variables: {
id: '1',
username: 'test@test.com',
password: 'password',
role: 'CustomerIT',
customerId: 2,
lastModifiedTimestamp: '1596814602673',
},
},
result: {
data: {
updateUser: {
id: '1',
username: 'test@test.com',
role: 'CustomerIT',
customerId: '2',
lastModifiedTimestamp: '1597080851726',
__typename: 'User',
},
},
},
},
editUserError: {
request: {
query: UPDATE_USER,
variables: {},
},
result: {
data: null,
errors: [{}],
},
},
deleteAccountSuccess: {
request: {
query: DELETE_USER,
variables: { id: '1' },
},
result: {
data: {
deleteUser: {
id: '1',
__typename: 'User',
},
},
},
},
deleteAccountError: {
request: {
query: DELETE_USER,
variables: { id: '2' },
},
result: {
data: null,
errors: [{}],
loading: false,
},
},
};

View File

@@ -1,35 +1,11 @@
import React, { useContext } from 'react';
import { AddProfile as AddProfilePage } from '@tip-wlan/wlan-cloud-ui-library';
import gql from 'graphql-tag';
import { useMutation, useQuery } from '@apollo/react-hooks';
import { notification } from 'antd';
import UserContext from 'contexts/UserContext';
import { GET_ALL_PROFILES } from 'graphql/queries';
const CREATE_PROFILE = gql`
mutation CreateProfile(
$profileType: String!
$customerId: ID!
$name: String!
$childProfileIds: [ID]
$details: JSONObject
) {
createProfile(
profileType: $profileType
customerId: $customerId
name: $name
childProfileIds: $childProfileIds
details: $details
) {
profileType
customerId
name
childProfileIds
details
}
}
`;
import { CREATE_PROFILE } from 'graphql/mutations';
const AddProfile = () => {
const { customerId } = useContext(UserContext);

View File

@@ -0,0 +1,75 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { addProfileMutationMock, getAllProfilesQueryMock } from './mock';
import AddProfile from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
const DOWN_ARROW = { keyCode: 40 };
describe('<AddProfile />', () => {
afterEach(cleanup);
it('should create profile when provide correct Mutation', async () => {
const { getByText, getByLabelText, getByRole } = render(
<MockedProvider
mocks={[getAllProfilesQueryMock.success, addProfileMutationMock.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<AddProfile />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByRole('button', { name: 'Save' })).toBeVisible());
fireEvent.change(getByLabelText('Name'), { target: { value: 'test' } });
await waitFor(() => expect(getByLabelText('Name')).toHaveValue('test'));
fireEvent.keyDown(getByLabelText('Type'), DOWN_ARROW);
fireEvent.click(getByText('Access Point'));
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Profile successfully created.')).toBeVisible());
});
it('should show error when provide incorrect Mutation', async () => {
const { getByText, getByLabelText, getByRole } = render(
<MockedProvider mocks={[getAllProfilesQueryMock.success]} addTypename={false}>
<UserProvider {...mockProp}>
<AddProfile />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByRole('button', { name: 'Save' })).toBeVisible());
fireEvent.change(getByLabelText('Name'), { target: { value: 'test' } });
await waitFor(() => expect(getByLabelText('Name')).toHaveValue('test'));
fireEvent.keyDown(getByLabelText('Type'), DOWN_ARROW);
fireEvent.click(getByText('Access Point'));
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Profile could not be created.')).toBeVisible());
});
});

View File

@@ -0,0 +1,146 @@
import { GET_ALL_PROFILES } from 'graphql/queries';
import { CREATE_PROFILE } from 'graphql/mutations';
export const getAllProfilesQueryMock = {
success: {
request: {
query: GET_ALL_PROFILES,
variables: { customerId: 2, type: 'ssid' },
},
result: {
data: {
getAllProfiles: {
items: [
{
id: '1',
name: 'Radius-Profile',
profileType: 'radius',
details: {
model_type: 'RadiusProfile',
subnetConfiguration: null,
serviceRegionMap: {
Ottawa: {
model_type: 'RadiusServiceRegion',
serverMap: {
'Radius-Profile': [
{
model_type: 'RadiusServer',
ipAddress: '192.168.0.1',
secret: 'testing123',
authPort: 1812,
timeout: null,
},
],
},
regionName: 'Ottawa',
},
},
profileType: 'radius',
},
__typename: 'Profile',
},
],
context: {
cursor:
'bnVsbEBAQHsibW9kZWxfdHlwZSI6IkNvbnRleHRDaGlsZHJlbiIsImNoaWxkcmVuIjp7fX1AQEBudWxs',
lastPage: true,
__typename: 'PaginationContext',
},
__typename: 'ProfilePagination',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
errpr: [],
};
export const addProfileMutationMock = {
success: {
request: {
query: CREATE_PROFILE,
variables: {
profileType: 'equipment_ap',
customerId: 2,
name: 'test',
childProfileIds: [],
details: {
profileType: 'equipment_ap',
name: 'test',
vlanNative: true,
ntpServer: { auto: undefined },
ledControlEnabled: undefined,
rtlsSettings: { enabled: false },
syslogRelay: { enabled: false },
syntheticClientEnabled: false,
equipmentDiscovery: false,
childProfileIds: [],
model_type: 'ApNetworkConfiguration',
},
},
},
result: {
data: {
createProfile: {
profileType: 'equipment_ap',
customerId: '2',
name: 'test',
childProfileIds: [],
details: {
model_type: 'ApNetworkConfiguration',
networkConfigVersion: 'AP-1',
equipmentType: 'AP',
vlanNative: true,
vlan: 0,
ntpServer: {
model_type: 'AutoOrManualString',
auto: false,
value: null,
},
syslogRelay: {
model_type: 'SyslogRelay',
enabled: false,
srvHostIp: null,
srvHostPort: 514,
severity: 'NOTICE',
},
rtlsSettings: {
model_type: 'RtlsSettings',
enabled: false,
srvHostIp: null,
srvHostPort: 0,
},
syntheticClientEnabled: false,
ledControlEnabled: true,
equipmentDiscovery: false,
radioMap: {
is5GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is2dot4GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is5GHzU: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is5GHzL: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
},
profileType: 'equipment_ap',
},
__typename: 'Profile',
},
},
},
},
};

View File

@@ -1,31 +1,10 @@
import React, { useContext } from 'react';
import gql from 'graphql-tag';
import { useQuery } from '@apollo/react-hooks';
import { Alert, notification } from 'antd';
import { Alarms as AlarmsPage, Loading } from '@tip-wlan/wlan-cloud-ui-library';
import UserContext from 'contexts/UserContext';
const GET_ALL_ALARMS = gql`
query GetAllAlarms($customerId: ID!, $cursor: String) {
getAllAlarms(customerId: $customerId, cursor: $cursor) {
items {
severity
alarmCode
details
createdTimestamp
equipment {
id
name
}
}
context {
cursor
lastPage
}
}
}
`;
import { GET_ALL_ALARMS } from 'graphql/queries';
const Alarms = () => {
const { customerId } = useContext(UserContext);

View File

@@ -0,0 +1,121 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { BrowserRouter as Router } from 'react-router-dom';
import UserProvider from 'contexts/UserProvider';
import { alarmsQueryMock } from './mock';
import Alarms from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<Alarms />', () => {
afterEach(jest.resetModules);
it('should render with Data', async () => {
const { getAllByText } = render(
<MockedProvider mocks={[alarmsQueryMock.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<Alarms />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getAllByText('Available memory is too low')[0]).toBeVisible());
});
it('should show error when query return error', async () => {
const { getByText } = render(
<MockedProvider mocks={[alarmsQueryMock.error]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<Alarms />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load alarms.')).toBeVisible());
});
it('should work with the onLoadMore', async () => {
const { getAllByText, getByRole } = render(
<MockedProvider
mocks={[alarmsQueryMock.success, alarmsQueryMock.loadmore]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Alarms />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getAllByText('Available memory is too low')[0]).toBeVisible());
fireEvent.click(getByRole('button', { name: /load more/i }));
await waitFor(() => expect(getAllByText('Available memory is too low')[0]).toBeVisible());
});
it('should work with the onReload', async () => {
const { getAllByText, getByText, container } = render(
<MockedProvider
mocks={[alarmsQueryMock.success, alarmsQueryMock.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Alarms />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getAllByText('Available memory is too low')[0]).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Alarms reloaded.')).toBeVisible());
});
it('should show error when reload wuery return error', async () => {
const { getAllByText, getByText, container } = render(
<MockedProvider mocks={[alarmsQueryMock.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<Alarms />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getAllByText('Available memory is too low')[0]).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Alarms reloaded.')).toBeVisible());
});
});

View File

@@ -0,0 +1,191 @@
import { GET_ALL_ALARMS } from 'graphql/queries';
export const alarmsQueryMock = {
success: {
request: {
query: GET_ALL_ALARMS,
variables: { customerId: 2 },
errorPolicy: 'all',
},
result: {
data: {
getAllAlarms: {
items: [
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564234684',
equipment: {
id: '7',
name: 'AP 7',
},
},
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564234796',
equipment: {
id: '14',
name: 'AP 14',
},
},
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564234885',
equipment: {
id: '21',
name: 'AP 21',
},
},
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564234957',
equipment: {
id: '28',
name: 'AP 28',
},
},
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564235022',
equipment: {
id: '35',
name: 'AP 35',
},
},
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564235082',
equipment: {
id: '42',
name: 'AP 42',
},
},
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564235155',
equipment: {
id: '49',
name: 'AP 49',
},
},
],
context: {
cursor: 'test',
lastPage: false,
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
loadmore: {
request: {
query: GET_ALL_ALARMS,
variables: { customerId: 2, cursor: 'test' },
errorPolicy: 'all',
},
result: {
data: {
getAllAlarms: {
items: [
{
severity: 'error',
alarmCode: 'MemoryUtilization',
details: {
model_type: 'AlarmDetails',
message: 'Available memory is too low',
affectedEquipmentIds: null,
generatedBy: null,
contextAttrs: null,
},
createdTimestamp: '1596564234684',
equipment: {
id: '7',
name: 'AP 7',
},
},
],
context: {
cursor: 'test2',
lastPage: true,
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_ALL_ALARMS,
variables: { customerId: 2 },
errorPolicy: 'all',
},
result: {
data: null,
loading: false,
errors: [{}],
networkStatus: 7,
stale: false,
},
},
};

View File

@@ -0,0 +1,68 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { dashboardQueryMock } from './mock';
import Dashboard from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
jest.mock('moment', () => () => ({
subtract: () => ({ valueOf: () => ({ toString: () => '1234' }) }),
valueOf: () => ({ toString: () => '1234' }),
}));
jest.useFakeTimers();
describe('<Dashboard />', () => {
afterEach(jest.resetModules);
it('should render with data', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
dashboardQueryMock.filterSystemEvents.success,
dashboardQueryMock.filterSystemEvents.success,
dashboardQueryMock.getAllStatus.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Dashboard />
</UserProvider>
</MockedProvider>
);
jest.advanceTimersByTime('300000');
await waitFor(() => expect(getByText('2.4GHz')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider mocks={[dashboardQueryMock.filterSystemEvents.error]} addTypename={false}>
<UserProvider {...mockProp}>
<Dashboard />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load Dashboard')).toBeVisible());
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,11 @@
import React, { useContext } from 'react';
import gql from 'graphql-tag';
import { useMutation, useQuery } from '@apollo/react-hooks';
import { notification, Alert } from 'antd';
import { EditAccount as EditAccountPage, Loading } from '@tip-wlan/wlan-cloud-ui-library';
import UserContext from 'contexts/UserContext';
const GET_USER = gql`
query GetUser($id: ID!) {
getUser(id: $id) {
id
username
role
customerId
lastModifiedTimestamp
}
}
`;
const UPDATE_USER = gql`
mutation UpdateUser(
$id: ID!
$username: String!
$password: String!
$role: String!
$customerId: ID!
$lastModifiedTimestamp: String
) {
updateUser(
id: $id
username: $username
password: $password
role: $role
customerId: $customerId
lastModifiedTimestamp: $lastModifiedTimestamp
) {
id
username
role
customerId
lastModifiedTimestamp
}
}
`;
import { GET_USER } from 'graphql/queries';
import { UPDATE_USER } from 'graphql/mutations';
const EditAccount = () => {
const { id, email } = useContext(UserContext);

View File

@@ -0,0 +1,86 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import UserProvider from 'contexts/UserProvider';
import { editAccountMutationMock, editAccountQueryMock } from './mock';
import EditAccount from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<EditAccount />', () => {
afterEach(jest.resetModules);
it('should render with Data and Edit Profile when valid Mutation', async () => {
const { getByText, getByTestId, getByLabelText } = render(
<MockedProvider
mocks={[editAccountQueryMock.success, editAccountMutationMock.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<EditAccount />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('New Password')).toBeVisible());
fireEvent.change(getByLabelText('New Password'), { target: { value: 'password' } });
await waitFor(() => expect(getByLabelText('New Password')).toHaveValue('password'));
fireEvent.change(getByLabelText('Confirm Password'), { target: { value: 'password' } });
fireEvent.submit(getByTestId('saveButton'));
await waitFor(() => expect(getByText('Password successfully updated.')).toBeVisible());
});
it('should show error when query return Error', async () => {
const { getByText } = render(
<MockedProvider mocks={[editAccountQueryMock.error]} addTypename={false}>
<UserProvider {...mockProp}>
<EditAccount />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load User.')).toBeVisible());
});
it('should show error when edit profile Mutation return error', async () => {
const { getByText, getByTestId, getByLabelText } = render(
<MockedProvider
mocks={[editAccountQueryMock.success, editAccountMutationMock.error]}
addTypename={false}
>
<UserProvider {...mockProp}>
<EditAccount />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('New Password')).toBeVisible());
fireEvent.change(getByLabelText('New Password'), { target: { value: 'password' } });
await waitFor(() => expect(getByLabelText('New Password')).toHaveValue('password'));
fireEvent.change(getByLabelText('Confirm Password'), { target: { value: 'password' } });
fireEvent.submit(getByTestId('saveButton'));
await waitFor(() => expect(getByText('Password could not be updated.')).toBeVisible());
});
});

View File

@@ -0,0 +1,85 @@
import { GET_USER } from 'graphql/queries';
import { UPDATE_USER } from 'graphql/mutations';
export const editAccountQueryMock = {
success: {
request: {
query: GET_USER,
variables: { id: 123 },
},
result: {
data: {
getUser: {
id: '123',
username: 'user-0',
role: 'CustomerIT',
customerId: '2',
lastModifiedTimestamp: '1597238767547',
__typename: 'User',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_USER,
variables: { id: 123 },
},
result: {
data: null,
errors: [{}],
loading: false,
networkStatus: 7,
stale: false,
},
},
};
export const editAccountMutationMock = {
success: {
request: {
query: UPDATE_USER,
variables: {
id: 123,
username: 'test@test.com',
password: 'password',
role: 'CustomerIT',
customerId: '2',
lastModifiedTimestamp: '1597238767547',
},
},
result: {
data: {
updateUser: {
id: 123,
username: 'user-0',
role: 'CustomerIT',
customerId: '2',
lastModifiedTimestamp: '1597238767547',
__typename: 'User',
},
},
},
},
error: {
request: {
query: UPDATE_USER,
variables: {
id: 123,
username: 'test@test.com',
password: 'password',
role: 'CustomerIT',
customerId: '2',
lastModifiedTimestamp: '1597238767547',
},
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -1,5 +1,4 @@
import React, { useContext } from 'react';
import gql from 'graphql-tag';
import { useMutation, useApolloClient } from '@apollo/react-hooks';
import { useHistory } from 'react-router-dom';
import { notification } from 'antd';
@@ -7,16 +6,7 @@ import { notification } from 'antd';
import { Login as LoginPage } from '@tip-wlan/wlan-cloud-ui-library';
import UserContext from 'contexts/UserContext';
const AUTHENTICATE_USER = gql`
mutation AuthenticateUser($email: String!, $password: String!) {
authenticateUser(email: $email, password: $password) {
access_token
refresh_token
expires_in
}
}
`;
import { AUTHENTICATE_USER } from 'graphql/mutations';
const Login = () => {
const history = useHistory();

View File

@@ -0,0 +1,71 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, cleanup, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import UserProvider from 'contexts/UserProvider';
import { ThemeProvider } from '@tip-wlan/wlan-cloud-ui-library';
import { loginMutationMock } from './mock';
import Login from '..';
jest.mock('react-router-dom', () => ({
useHistory: () => ({ push: jest.fn() }),
}));
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<Login />', () => {
afterEach(() => cleanup);
it('login should be successful when mutation is valid', async () => {
const { getByLabelText, getByTestId } = render(
<ThemeProvider company="Test" logo="test.png" logoMobile="test.png">
<MockedProvider mocks={[loginMutationMock.loginSuccess]} addTypename={false}>
<UserProvider {...mockProp}>
<Login />
</UserProvider>
</MockedProvider>
</ThemeProvider>
);
fireEvent.change(getByLabelText('E-mail'), { target: { value: 'test@test.com' } });
fireEvent.change(getByLabelText('Password'), { target: { value: 'password' } });
fireEvent.submit(getByTestId('loginButton'));
await waitFor(() => expect(getByLabelText('E-mail')).toBeVisible());
});
it('login should not be successful when mutation is invalid', async () => {
const { getByLabelText, getByText, getByTestId } = render(
<ThemeProvider company="Test" logo="test.png" logoMobile="test.png">
<MockedProvider mocks={[loginMutationMock.loginError]} addTypename={false}>
<UserProvider {...mockProp}>
<Login />
</UserProvider>
</MockedProvider>
</ThemeProvider>
);
fireEvent.change(getByLabelText('E-mail'), { target: { value: 'test@test.com' } });
fireEvent.change(getByLabelText('Password'), { target: { value: 'password' } });
fireEvent.submit(getByTestId('loginButton'));
await waitFor(() => expect(getByText('Invalid e-mail or password.')).toBeVisible());
});
});

View File

@@ -0,0 +1,32 @@
import { AUTHENTICATE_USER } from 'graphql/mutations';
export const loginMutationMock = {
loginSuccess: {
request: {
query: AUTHENTICATE_USER,
variables: {
email: 'test@test.com',
password: 'password',
},
},
result: {
data: {
authenticateUser: {
access_token: '123',
refresh_token: '345',
expires_in: '1',
},
},
},
},
loginError: {
request: {
query: AUTHENTICATE_USER,
variables: {},
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { useParams } from 'react-router-dom';
import gql from 'graphql-tag';
import { useParams } from 'react-router-dom';
import { useQuery, useMutation } from '@apollo/react-hooks';
import { Alert, notification } from 'antd';
import moment from 'moment';
@@ -10,129 +10,10 @@ import {
Loading,
} from '@tip-wlan/wlan-cloud-ui-library';
import { FILTER_SERVICE_METRICS } from 'graphql/queries';
import { UPDATE_EQUIPMENT_FIRMWARE } from 'graphql/mutations';
import { GET_EQUIPMENT, GET_ALL_FIRMWARE, FILTER_SERVICE_METRICS } from 'graphql/queries';
import { UPDATE_EQUIPMENT, UPDATE_EQUIPMENT_FIRMWARE } from 'graphql/mutations';
import UserContext from 'contexts/UserContext';
const GET_EQUIPMENT = gql`
query GetEquipment($id: ID!) {
getEquipment(id: $id) {
id
equipmentType
inventoryId
customerId
profileId
locationId
name
latitude
longitude
serial
lastModifiedTimestamp
details
profile {
id
name
childProfiles {
id
name
details
}
}
status {
firmware {
detailsJSON
}
protocol {
detailsJSON
details {
reportedMacAddr
manufacturer
}
}
radioUtilization {
detailsJSON
}
clientDetails {
detailsJSON
details {
numClientsPerRadio
}
}
osPerformance {
detailsJSON
}
}
model
alarmsCount
alarms {
severity
alarmCode
details
createdTimestamp
}
}
}
`;
export const GET_ALL_FIRMWARE = gql`
query GetAllFirmware {
getAllFirmware {
id
modelId
versionName
description
filename
commit
releaseDate
}
}
`;
const UPDATE_EQUIPMENT = gql`
mutation UpdateEquipment(
$id: ID!
$equipmentType: String!
$inventoryId: String!
$customerId: ID!
$profileId: ID!
$locationId: ID!
$name: String!
$latitude: String
$longitude: String
$serial: String
$lastModifiedTimestamp: String
$details: JSONObject
) {
updateEquipment(
id: $id
equipmentType: $equipmentType
inventoryId: $inventoryId
customerId: $customerId
profileId: $profileId
locationId: $locationId
name: $name
latitude: $latitude
longitude: $longitude
serial: $serial
lastModifiedTimestamp: $lastModifiedTimestamp
details: $details
) {
id
equipmentType
inventoryId
customerId
profileId
locationId
name
latitude
longitude
serial
lastModifiedTimestamp
details
}
}
`;
export const GET_ALL_PROFILES = gql`
query GetAllProfiles($customerId: ID!, $cursor: String, $type: String, $limit: Int) {
getAllProfiles(customerId: $customerId, cursor: $cursor, type: $type, limit: $limit) {

View File

@@ -0,0 +1,106 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { APDetailsQueryMock } from './mock';
import AccessPointDetails from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
jest.mock('moment', () => () => ({
subtract: () => ({ valueOf: () => ({ toString: () => '1234' }) }),
valueOf: () => ({ toString: () => '1234' }),
}));
jest.mock('react-router-dom', () => ({
useParams: () => ({
id: '1',
}),
useHistory: () => ({ push: jest.fn() }),
}));
jest.useFakeTimers();
describe('<AccessPointDetails />', () => {
afterEach(jest.resetModules);
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
APDetailsQueryMock.getEquipment.error,
APDetailsQueryMock.getAllProfiles.error,
APDetailsQueryMock.getAllFirmware.error,
APDetailsQueryMock.filterServiceMetrics.error,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<AccessPointDetails locations={[1, 2, 3]} />
</UserProvider>
</MockedProvider>
);
jest.advanceTimersByTime(1000);
await waitFor(() => expect(getByText('Failed to load Access Point data.')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
APDetailsQueryMock.getEquipment.success,
APDetailsQueryMock.getAllProfiles.error,
APDetailsQueryMock.getAllFirmware.success,
APDetailsQueryMock.filterServiceMetrics.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<AccessPointDetails locations={[1, 2, 3]} />
</UserProvider>
</MockedProvider>
);
jest.advanceTimersByTime(1000);
await waitFor(() => expect(getByText('Failed to load Access Point profiles.')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
APDetailsQueryMock.getEquipment.success,
APDetailsQueryMock.getAllProfiles.success,
APDetailsQueryMock.getAllFirmware.error,
APDetailsQueryMock.filterServiceMetrics.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<AccessPointDetails locations={[1, 2, 3]} />
</UserProvider>
</MockedProvider>
);
jest.advanceTimersByTime(1000);
await waitFor(() => expect(getByText('Failed to load Access Point firmware.')).toBeVisible());
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { fireEvent, render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { BrowserRouter as Router } from 'react-router-dom';
import { AccessPointsQueryMock } from './mock';
import AccessPoints from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<AccessPoints />', () => {
afterEach(jest.resetModules);
it('should render with data', async () => {
const { getByText } = render(
<MockedProvider mocks={[AccessPointsQueryMock.filterEquipment.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<AccessPoints checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('AP 1')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider mocks={[AccessPointsQueryMock.filterEquipment.error]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<AccessPoints checkedLocations={[1, 2, 3]} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load equipment.')).toBeVisible());
});
it('click on Load More should fetch more data', async () => {
const { getByRole, getByText } = render(
<MockedProvider
mocks={[
AccessPointsQueryMock.filterEquipment.success,
AccessPointsQueryMock.filterEquipment.loadmore,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<AccessPoints checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('AP 1')).toBeVisible());
fireEvent.click(getByRole('button', { name: /load more/i }));
await waitFor(() => expect(getByText('AP 1')).toBeVisible());
});
it('click on reload button should refetch data', async () => {
const { getByText, container } = render(
<MockedProvider
mocks={[
AccessPointsQueryMock.filterEquipment.success,
AccessPointsQueryMock.filterEquipment.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<AccessPoints checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('AP 1')).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Access points reloaded.')).toBeVisible());
});
it('click on reload button should not refetch data when query return error', async () => {
const { getByText, container } = render(
<MockedProvider mocks={[AccessPointsQueryMock.filterEquipment.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<AccessPoints checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('AP 1')).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Access points could not be reloaded.')).toBeVisible());
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { BulkEditAPsQueryMock } from './mock';
import BulkEditAPs from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
const locations = [
{
children: [
{
children: [
{
id: '4',
key: '4',
locationType: 'FLOOR',
name: 'Floor 1',
parentId: '3',
title: '',
value: '4',
__typename: 'Location',
},
{
id: '5',
key: '5',
locationType: 'FLOOR',
name: 'Floor 2',
parentId: '3',
title: '',
value: '5',
__typename: 'Location',
},
{
id: '6',
key: '6',
locationType: 'FLOOR',
name: 'Floor 3',
parentId: '3',
title: '',
value: '6',
__typename: 'Location',
},
],
id: '3',
key: '3',
locationType: 'BUILDING',
name: 'Building 1',
parentId: '2',
title: '',
value: '3',
__typename: 'Location',
},
{
id: '7',
key: '7',
locationType: 'BUILDING',
name: 'Building 2',
parentId: '2',
title: '',
value: '7',
__typename: 'Location',
},
],
id: '2',
key: '2',
locationType: 'SITE',
name: 'Menlo Park',
parentId: '0',
title: '',
value: '2',
__typename: 'Location',
},
{
id: '8',
key: '8',
locationType: 'SITE',
name: 'Ottawa',
parentId: '0',
title: 'test',
value: '8',
__typename: 'Location',
},
];
jest.mock('react-router-dom', () => ({
useParams: () => ({
id: '6',
}),
useHistory: () => ({ push: jest.fn() }),
}));
describe('<BulkEditAPs />', () => {
afterEach(jest.resetModules);
it('should render with data', async () => {
const { getByText } = render(
<MockedProvider
mocks={[BulkEditAPsQueryMock.filterEquipmentBulkEditAps.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<BulkEditAPs
locations={locations}
checkedLocations={['2', '3', '4', '5', '6', '7', '8']}
/>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('AP 1')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider
mocks={[BulkEditAPsQueryMock.filterEquipmentBulkEditAps.error]}
addTypename={false}
>
<UserProvider {...mockProp}>
<BulkEditAPs
locations={locations}
checkedLocations={['2', '3', '4', '5', '6', '7', '8']}
/>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load equipments data.')).toBeVisible());
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { fireEvent, render, waitFor, cleanup } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { ClientDevicesDetailsQueryMock } from './mock';
import ClientDeviceDetails from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
jest.mock('moment', () => () => ({
subtract: () => ({ valueOf: () => ({ toString: () => '1234' }) }),
valueOf: () => ({ toString: () => '1234' }),
}));
jest.mock('react-router-dom', () => ({
useParams: () => ({
id: '74:9c:00:01:45:ae',
}),
useHistory: () => ({ push: jest.fn() }),
}));
jest.useFakeTimers();
describe('<ClientDeviceDetails />', () => {
afterEach(cleanup);
beforeEach(() => jest.useFakeTimers());
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider
mocks={[ClientDevicesDetailsQueryMock.getClientSession.error]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ClientDeviceDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load Client Device.')).toBeVisible());
});
it('should render with data', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
ClientDevicesDetailsQueryMock.getClientSession.success,
ClientDevicesDetailsQueryMock.filterServiceMetrics.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ClientDeviceDetails />
</UserProvider>
</MockedProvider>
);
// jest.advanceTimersByTime('1000');
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
});
it.only('click on reload button should refetch data', async () => {
const { getByText, container } = render(
<MockedProvider
mocks={[
ClientDevicesDetailsQueryMock.getClientSession.success,
ClientDevicesDetailsQueryMock.filterServiceMetrics.success,
ClientDevicesDetailsQueryMock.getClientSession.success,
ClientDevicesDetailsQueryMock.filterServiceMetrics.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ClientDeviceDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
// await waitFor(() => expect(getByText('hostName-128213363803566')).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Successfully reloaded.')).toBeVisible());
});
// it('error message should be visible with error true', async () => {
// const { getByText } = render(
// <MockedProvider
// mocks={[
// ClientDevicesDetailsQueryMock.getClientSession.success,
// ClientDevicesDetailsQueryMock.filterServiceMetrics.success,
// ClientDevicesDetailsQueryMock.filterServiceMetrics.success,
// ClientDevicesDetailsQueryMock.getClientSession.success,
// ]}
// addTypename={false}
// >
// <UserProvider {...mockProp}>
// <ClientDeviceDetails />
// </UserProvider>
// </MockedProvider>
// );
// jest.advanceTimersByTime(60000);
// });
});

View File

@@ -0,0 +1,183 @@
import { GET_CLIENT_SESSION, FILTER_SERVICE_METRICS } from 'graphql/queries';
export const ClientDevicesDetailsQueryMock = {
getClientSession: {
success: {
request: {
query: GET_CLIENT_SESSION,
variables: {
customerId: 2,
macAddress: '74:9c:00:01:45:ae',
},
},
result: {
data: {
getClientSession: [
{
id: '74:9c:00:01:45:ae',
macAddress: '74:9c:00:01:45:ae',
ipAddress: '192.168.10.171',
hostname: 'hostName-128213363803566',
ssid: 'Default-SSID-1597172801679',
radioType: 'is2dot4GHz',
signal: '-56',
manufacturer: null,
equipment: {
name: 'AP 40',
__typename: 'Equipment',
},
details: {
model_type: 'ClientSessionDetails',
sessionId: 1597172802575,
authTimestamp: null,
assocTimestamp: 1597172185365,
assocInternalSC: null,
ipTimestamp: null,
disconnectByApTimestamp: null,
disconnectByClientTimestamp: null,
timeoutTimestamp: null,
firstDataSentTimestamp: null,
firstDataRcvdTimestamp: null,
ipAddress: '192.168.10.171',
radiusUsername: null,
ssid: 'Default-SSID-1597172801679',
radioType: 'is2dot4GHz',
lastEventTimestamp: 0,
hostname: 'hostName-128213363803566',
apFingerprint: 'fp 74:9c:00:01:45:ae',
userAgentStr: null,
lastRxTimestamp: null,
lastTxTimestamp: null,
cpUsername: null,
dhcpDetails: {
model_type: 'ClientDhcpDetails',
dhcpServerIp: '192.168.0.1',
primaryDns: '8.8.8.8',
secondaryDns: '192.168.0.1',
subnetMask: '192.168.0.255',
gatewayIp: '192.168.0.1',
leaseStartTimestamp: 1597160501039,
leaseTimeInSeconds: 14400,
firstRequestTimestamp: null,
firstOfferTimestamp: null,
firstDiscoverTimestamp: null,
nakTimestamp: null,
fromInternal: false,
associationId: 1597172802575,
},
eapDetails: null,
metricDetails: {
model_type: 'ClientSessionMetricDetails',
rxBytes: 6048860,
txBytes: 6400949,
totalRxPackets: null,
totalTxPackets: null,
rxMbps: 91.4453,
txMbps: 65.76753,
rssi: -56,
snr: -51,
rxRateKbps: null,
txRateKbps: null,
lastMetricTimestamp: 0,
lastRxTimestamp: null,
lastTxTimestamp: null,
classification: null,
txDataFrames: null,
txDataFramesRetried: null,
rxDataFrames: null,
},
isReassociation: null,
disconnectByApReasonCode: null,
disconnectByClientReasonCode: null,
disconnectByApInternalReasonCode: null,
disconnectByClientInternalReasonCode: null,
portEnabledTimestamp: null,
is11RUsed: null,
is11KUsed: null,
is11VUsed: null,
securityType: 'PSK',
steerType: null,
previousValidSessionId: null,
lastFailureDetails: null,
firstFailureDetails: null,
associationStatus: null,
dynamicVlan: null,
assocRssi: null,
priorSessionId: null,
priorEquipmentId: null,
classificationName: null,
associationState: null,
eapSuccessTimestamp: null,
eapKey4Timestamp: null,
},
__typename: 'ClientSession',
},
],
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_CLIENT_SESSION,
variables: { customerId: 2 },
errorPolicy: 'all',
},
result: {
data: null,
errors: [{}],
loading: false,
networkStatus: 7,
stale: false,
},
},
},
filterServiceMetrics: {
success: {
request: {
query: FILTER_SERVICE_METRICS,
variables: {
customerId: 2,
fromTime: '1234',
toTime: '1234',
clientMacs: ['74:9c:00:01:45:ae'],
dataTypes: ['Client'],
limit: 1000,
},
},
result: {
data: {
filterServiceMetrics: {
items: [],
context: {
lastPage: true,
cursor:
'bnVsbEBAQHsibW9kZWxfdHlwZSI6IkNvbnRleHRDaGlsZHJlbiIsImNoaWxkcmVuIjp7fX1AQEBudWxs',
__typename: 'PaginationContext',
},
__typename: 'ServiceMetricPagination',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: FILTER_SERVICE_METRICS,
variables: { customerId: 2 },
errorPolicy: 'all',
},
result: {
data: null,
errors: [{}],
loading: false,
networkStatus: 7,
stale: false,
},
},
},
};

View File

@@ -0,0 +1,138 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { MockedProvider } from '@apollo/react-testing';
import { fireEvent, render, waitFor } from '@testing-library/react';
import UserProvider from 'contexts/UserProvider';
import { BrowserRouter as Router } from 'react-router-dom';
import { ClientDevicesQueryMock } from './mock';
import ClientDevices from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<ClientDevices />', () => {
afterEach(jest.resetModules);
it('should render with data', async () => {
const { getByText } = render(
<MockedProvider
mocks={[ClientDevicesQueryMock.filterClientSessions.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<ClientDevices checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
await waitFor(() => expect(getByText('hostName-128213363803566')).toBeVisible());
});
it('error message should be visible with error true', async () => {
const { getByText } = render(
<MockedProvider
mocks={[ClientDevicesQueryMock.filterClientSessions.error]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<ClientDevices checkedLocations={[1, 2, 3]} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load client devices.')).toBeVisible());
});
it('click on Load More should fetch more data', async () => {
const { getByRole, getByText, getAllByText } = render(
<MockedProvider
mocks={[
ClientDevicesQueryMock.filterClientSessions.success,
ClientDevicesQueryMock.filterClientSessions.loadmore,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<ClientDevices checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
await waitFor(() => expect(getByText('hostName-128213363803566')).toBeVisible());
fireEvent.click(getByRole('button', { name: /load more/i }));
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
await waitFor(() => expect(getAllByText('hostName-128213363803566')[1]).toBeVisible());
});
it('click on reload button should refetch data', async () => {
const { getByText, container } = render(
<MockedProvider
mocks={[
ClientDevicesQueryMock.filterClientSessions.success,
ClientDevicesQueryMock.filterClientSessions.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<ClientDevices checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
await waitFor(() => expect(getByText('hostName-128213363803566')).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Client devices reloaded.')).toBeVisible());
});
it('click on reload button should not refetch data when query return error', async () => {
const { getByText, container } = render(
<MockedProvider
mocks={[ClientDevicesQueryMock.filterClientSessions.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<ClientDevices checkedLocations={['2', '3', '4', '5', '6', '7', '8']} />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
await waitFor(() => expect(getByText('hostName-128213363803566')).toBeVisible());
const reloadButton = container.querySelector(
'.ant-btn.index-module__Button___VGygY.ant-btn-icon-only'
);
fireEvent.click(reloadButton);
await waitFor(() => expect(getByText('Client devices could not be reloaded.')).toBeVisible());
});
});

View File

@@ -0,0 +1,591 @@
import { FILTER_CLIENT_SESSIONS } from 'graphql/queries';
export const ClientDevicesQueryMock = {
filterClientSessions: {
success: {
request: {
query: FILTER_CLIENT_SESSIONS,
variables: {
customerId: 2,
locationIds: ['2', '3', '4', '5', '6', '7', '8'],
equipmentType: 'AP',
},
},
result: {
data: {
filterClientSessions: {
items: [
{
id: '74:9c:00:01:45:ae',
macAddress: '74:9c:00:01:45:ae',
ipAddress: '192.168.10.171',
hostname: 'hostName-128213363803566',
ssid: 'Default-SSID-1597172801679',
radioType: 'is2dot4GHz',
signal: '-56',
manufacturer: null,
equipment: {
name: 'AP 40',
},
},
{
id: '74:9c:00:8a:70:8b',
macAddress: '74:9c:00:8a:70:8b',
ipAddress: '192.168.10.159',
hostname: 'hostName-128213372792971',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 2',
},
},
{
id: '74:9c:00:ae:78:0b',
macAddress: '74:9c:00:ae:78:0b',
ipAddress: '192.168.10.21',
hostname: 'hostName-128213375154187',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzL',
signal: '-56',
manufacturer: null,
equipment: {
name: 'AP 37',
},
},
{
id: '74:9c:00:b8:6e:6b',
macAddress: '74:9c:00:b8:6e:6b',
ipAddress: '192.168.10.145',
hostname: 'hostName-128213375807083',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-55',
manufacturer: null,
equipment: {
name: 'AP 9',
},
},
{
id: '74:9c:00:da:28:86',
macAddress: '74:9c:00:da:28:86',
ipAddress: '192.168.10.136',
hostname: 'hostName-128213378017414',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzL',
signal: '-54',
manufacturer: null,
equipment: {
name: 'AP 34',
},
},
{
id: '74:9c:01:20:7f:35',
macAddress: '74:9c:01:20:7f:35',
ipAddress: '192.168.10.132',
hostname: 'hostName-128213382627125',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzU',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 37',
},
},
{
id: '74:9c:01:4b:bd:77',
macAddress: '74:9c:01:4b:bd:77',
ipAddress: '192.168.10.114',
hostname: 'hostName-128213385461111',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 19',
},
},
{
id: '74:9c:01:77:97:6c',
macAddress: '74:9c:01:77:97:6c',
ipAddress: '192.168.10.35',
hostname: 'hostName-128213388334956',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-52',
manufacturer: null,
equipment: {
name: 'AP 26',
},
},
{
id: '74:9c:01:9c:29:be',
macAddress: '74:9c:01:9c:29:be',
ipAddress: '192.168.10.174',
hostname: 'hostName-128213390731710',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-44',
manufacturer: null,
equipment: {
name: 'AP 6',
},
},
{
id: '74:9c:01:bf:ce:af',
macAddress: '74:9c:01:bf:ce:af',
ipAddress: '192.168.10.36',
hostname: 'hostName-128213393067695',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-60',
manufacturer: null,
equipment: {
name: 'AP 31',
},
},
{
id: '74:9c:02:41:bf:2a',
macAddress: '74:9c:02:41:bf:2a',
ipAddress: '192.168.10.202',
hostname: 'hostName-128213401583402',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-42',
manufacturer: null,
equipment: {
name: 'AP 29',
},
},
{
id: '74:9c:02:86:89:6a',
macAddress: '74:9c:02:86:89:6a',
ipAddress: '192.168.10.77',
hostname: 'hostName-128213406091626',
ssid: 'Default-SSID-1597172801679',
radioType: 'is2dot4GHz',
signal: '-54',
manufacturer: null,
equipment: {
name: 'AP 38',
},
},
{
id: '74:9c:02:8f:14:91',
macAddress: '74:9c:02:8f:14:91',
ipAddress: '192.168.10.144',
hostname: 'hostName-128213406651537',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-60',
manufacturer: null,
equipment: {
name: 'AP 3',
},
},
{
id: '74:9c:02:9f:25:27',
macAddress: '74:9c:02:9f:25:27',
ipAddress: '192.168.10.125',
hostname: 'hostName-128213407704359',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-45',
manufacturer: null,
equipment: {
name: 'AP 3',
},
},
{
id: '74:9c:03:12:cd:00',
macAddress: '74:9c:03:12:cd:00',
ipAddress: '192.168.10.214',
hostname: 'hostName-128213415283968',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-46',
manufacturer: null,
equipment: {
name: 'AP 14',
},
},
{
id: '74:9c:03:47:bc:43',
macAddress: '74:9c:03:47:bc:43',
ipAddress: '192.168.10.187',
hostname: 'hostName-128213418753091',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-49',
manufacturer: null,
equipment: {
name: 'AP 3',
},
},
{
id: '74:9c:03:73:2d:d0',
macAddress: '74:9c:03:73:2d:d0',
ipAddress: '192.168.10.107',
hostname: 'hostName-128213421600208',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-49',
manufacturer: null,
equipment: {
name: 'AP 17',
},
},
{
id: '74:9c:04:1f:a0:26',
macAddress: '74:9c:04:1f:a0:26',
ipAddress: '192.168.10.48',
hostname: 'hostName-128213432901670',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-44',
manufacturer: null,
equipment: {
name: 'AP 10',
},
},
{
id: '74:9c:04:61:19:c8',
macAddress: '74:9c:04:61:19:c8',
ipAddress: '192.168.10.218',
hostname: 'hostName-128213437192648',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzL',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 48',
},
},
{
id: '74:9c:04:7e:5c:ae',
macAddress: '74:9c:04:7e:5c:ae',
ipAddress: '192.168.10.92',
hostname: 'hostName-128213439110318',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-43',
manufacturer: null,
equipment: {
name: 'AP 5',
},
},
],
context: {
lastPage: false,
cursor: 'test',
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
loadmore: {
request: {
query: FILTER_CLIENT_SESSIONS,
variables: {
customerId: 2,
locationIds: ['2', '3', '4', '5', '6', '7', '8'],
equipmentType: 'AP',
cursor: 'test',
},
},
result: {
data: {
filterClientSessions: {
items: [
{
id: '74:9c:00:01:45:ae',
macAddress: '74:9c:00:01:45:ae',
ipAddress: '192.168.10.171',
hostname: 'hostName-128213363803566',
ssid: 'Default-SSID-1597172801679',
radioType: 'is2dot4GHz',
signal: '-56',
manufacturer: null,
equipment: {
name: 'AP 40',
},
},
{
id: '74:9c:00:8a:70:8b',
macAddress: '74:9c:00:8a:70:8b',
ipAddress: '192.168.10.159',
hostname: 'hostName-128213372792971',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 2',
},
},
{
id: '74:9c:00:ae:78:0b',
macAddress: '74:9c:00:ae:78:0b',
ipAddress: '192.168.10.21',
hostname: 'hostName-128213375154187',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzL',
signal: '-56',
manufacturer: null,
equipment: {
name: 'AP 37',
},
},
{
id: '74:9c:00:b8:6e:6b',
macAddress: '74:9c:00:b8:6e:6b',
ipAddress: '192.168.10.145',
hostname: 'hostName-128213375807083',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-55',
manufacturer: null,
equipment: {
name: 'AP 9',
},
},
{
id: '74:9c:00:da:28:86',
macAddress: '74:9c:00:da:28:86',
ipAddress: '192.168.10.136',
hostname: 'hostName-128213378017414',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzL',
signal: '-54',
manufacturer: null,
equipment: {
name: 'AP 34',
},
},
{
id: '74:9c:01:20:7f:35',
macAddress: '74:9c:01:20:7f:35',
ipAddress: '192.168.10.132',
hostname: 'hostName-128213382627125',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzU',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 37',
},
},
{
id: '74:9c:01:4b:bd:77',
macAddress: '74:9c:01:4b:bd:77',
ipAddress: '192.168.10.114',
hostname: 'hostName-128213385461111',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 19',
},
},
{
id: '74:9c:01:77:97:6c',
macAddress: '74:9c:01:77:97:6c',
ipAddress: '192.168.10.35',
hostname: 'hostName-128213388334956',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-52',
manufacturer: null,
equipment: {
name: 'AP 26',
},
},
{
id: '74:9c:01:9c:29:be',
macAddress: '74:9c:01:9c:29:be',
ipAddress: '192.168.10.174',
hostname: 'hostName-128213390731710',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-44',
manufacturer: null,
equipment: {
name: 'AP 6',
},
},
{
id: '74:9c:01:bf:ce:af',
macAddress: '74:9c:01:bf:ce:af',
ipAddress: '192.168.10.36',
hostname: 'hostName-128213393067695',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-60',
manufacturer: null,
equipment: {
name: 'AP 31',
},
},
{
id: '74:9c:02:41:bf:2a',
macAddress: '74:9c:02:41:bf:2a',
ipAddress: '192.168.10.202',
hostname: 'hostName-128213401583402',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-42',
manufacturer: null,
equipment: {
name: 'AP 29',
},
},
{
id: '74:9c:02:86:89:6a',
macAddress: '74:9c:02:86:89:6a',
ipAddress: '192.168.10.77',
hostname: 'hostName-128213406091626',
ssid: 'Default-SSID-1597172801679',
radioType: 'is2dot4GHz',
signal: '-54',
manufacturer: null,
equipment: {
name: 'AP 38',
},
},
{
id: '74:9c:02:8f:14:91',
macAddress: '74:9c:02:8f:14:91',
ipAddress: '192.168.10.144',
hostname: 'hostName-128213406651537',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-60',
manufacturer: null,
equipment: {
name: 'AP 3',
},
},
{
id: '74:9c:02:9f:25:27',
macAddress: '74:9c:02:9f:25:27',
ipAddress: '192.168.10.125',
hostname: 'hostName-128213407704359',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-45',
manufacturer: null,
equipment: {
name: 'AP 3',
},
},
{
id: '74:9c:03:12:cd:00',
macAddress: '74:9c:03:12:cd:00',
ipAddress: '192.168.10.214',
hostname: 'hostName-128213415283968',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is2dot4GHz',
signal: '-46',
manufacturer: null,
equipment: {
name: 'AP 14',
},
},
{
id: '74:9c:03:47:bc:43',
macAddress: '74:9c:03:47:bc:43',
ipAddress: '192.168.10.187',
hostname: 'hostName-128213418753091',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-49',
manufacturer: null,
equipment: {
name: 'AP 3',
},
},
{
id: '74:9c:03:73:2d:d0',
macAddress: '74:9c:03:73:2d:d0',
ipAddress: '192.168.10.107',
hostname: 'hostName-128213421600208',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-49',
manufacturer: null,
equipment: {
name: 'AP 17',
},
},
{
id: '74:9c:04:1f:a0:26',
macAddress: '74:9c:04:1f:a0:26',
ipAddress: '192.168.10.48',
hostname: 'hostName-128213432901670',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzL',
signal: '-44',
manufacturer: null,
equipment: {
name: 'AP 10',
},
},
{
id: '74:9c:04:61:19:c8',
macAddress: '74:9c:04:61:19:c8',
ipAddress: '192.168.10.218',
hostname: 'hostName-128213437192648',
ssid: 'Default-SSID-1597172801679',
radioType: 'is5GHzL',
signal: '-53',
manufacturer: null,
equipment: {
name: 'AP 48',
},
},
{
id: '74:9c:04:7e:5c:ae',
macAddress: '74:9c:04:7e:5c:ae',
ipAddress: '192.168.10.92',
hostname: 'hostName-128213439110318',
ssid: 'TipWlan-cloud-3-radios',
radioType: 'is5GHzU',
signal: '-43',
manufacturer: null,
equipment: {
name: 'AP 5',
},
},
],
context: {
lastPage: false,
cursor: 'test',
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: FILTER_CLIENT_SESSIONS,
variables: { customerId: 2 },
errorPolicy: 'all',
},
result: {
data: null,
errors: [{}],
loading: false,
networkStatus: 7,
stale: false,
},
},
},
};

View File

@@ -1,72 +1,12 @@
import React, { useState, useContext } from 'react';
import { useParams, Redirect } from 'react-router-dom';
import gql from 'graphql-tag';
import { useQuery, useMutation } from '@apollo/react-hooks';
import { Alert, notification } from 'antd';
import { ProfileDetails as ProfileDetailsPage, Loading } from '@tip-wlan/wlan-cloud-ui-library';
import UserContext from 'contexts/UserContext';
import { GET_ALL_PROFILES } from 'graphql/queries';
import { FILE_UPLOAD } from 'graphql/mutations';
const GET_PROFILE = gql`
query GetProfile($id: ID!) {
getProfile(id: $id) {
id
profileType
customerId
name
childProfiles {
id
name
profileType
details
}
childProfileIds
createdTimestamp
lastModifiedTimestamp
details
}
}
`;
const UPDATE_PROFILE = gql`
mutation UpdateProfile(
$id: ID!
$profileType: String!
$customerId: ID!
$name: String!
$childProfileIds: [ID]
$lastModifiedTimestamp: String
$details: JSONObject
) {
updateProfile(
id: $id
profileType: $profileType
customerId: $customerId
name: $name
childProfileIds: $childProfileIds
lastModifiedTimestamp: $lastModifiedTimestamp
details: $details
) {
id
profileType
customerId
name
childProfileIds
lastModifiedTimestamp
details
}
}
`;
const DELETE_PROFILE = gql`
mutation DeleteProfile($id: ID!) {
deleteProfile(id: $id) {
id
}
}
`;
import { GET_PROFILE, GET_ALL_PROFILES } from 'graphql/queries';
import { FILE_UPLOAD, UPDATE_PROFILE, DELETE_PROFILE } from 'graphql/mutations';
const ProfileDetails = () => {
const { customerId } = useContext(UserContext);

View File

@@ -0,0 +1,186 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import UserProvider from 'contexts/UserProvider';
import { getAllProfilesQueryMock } from '../../AddProfile/tests/mock';
import { profileDetailsMutationMock, profileDetailsQueryMock } from './mock';
import ProfileDetails from '..';
jest.mock('rc-upload/lib/uid.js', () => ({
__esModule: true,
default: () => '1234',
}));
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
jest.mock('react-router-dom', () => ({
useParams: () => ({
id: 123,
}),
useHistory: () => ({ push: jest.fn() }),
}));
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<ProfileDetails />', () => {
afterEach(jest.resetModules);
it('should render with Data', async () => {
const { getByLabelText } = render(
<MockedProvider
mocks={[profileDetailsQueryMock.success, getAllProfilesQueryMock.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ProfileDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByLabelText('Profile Name')).toHaveValue('Radius-Profile'));
});
it('should show error when query return error', async () => {
const { getByText } = render(
<MockedProvider mocks={[profileDetailsQueryMock.error]} addTypename={false}>
<UserProvider {...mockProp}>
<ProfileDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load profile data.')).toBeVisible());
});
it('should render with Data and update successfully', async () => {
const { getByLabelText, getByText, getByRole } = render(
<MockedProvider
mocks={[
profileDetailsQueryMock.success,
getAllProfilesQueryMock.success,
profileDetailsMutationMock.updateSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ProfileDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByLabelText('Profile Name')).toHaveValue('Radius-Profile'));
await waitFor(() => expect(getByText('0.0.0.0')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Profile successfully updated.')).toBeVisible());
});
it('should render with Data and show error when update mutation return error', async () => {
const { getByLabelText, getByText, getByRole } = render(
<MockedProvider
mocks={[
profileDetailsQueryMock.success,
getAllProfilesQueryMock.success,
profileDetailsMutationMock.updateError,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ProfileDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByLabelText('Profile Name')).toHaveValue('Radius-Profile'));
await waitFor(() => expect(getByText('0.0.0.0')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Profile could not be updated.')).toBeVisible());
});
it('should render with Data and upload image successfully', async () => {
global.URL.createObjectURL = jest.fn();
const { getByLabelText, getByTestId, getByText } = render(
<MockedProvider
mocks={[
profileDetailsQueryMock.successCaptivePortal,
getAllProfilesQueryMock.success,
profileDetailsMutationMock.uploadSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ProfileDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByLabelText('Profile Name')).toHaveValue('Captive-portal'));
fireEvent.change(getByTestId('logoFile'), {
target: {
files: [
{
lastModified: 1595008730671,
lastModifiedDate: undefined,
name: 'testImg.jpg',
size: 100,
type: 'image/jpg',
percent: 0,
originFileObj: { uid: 'rc-upload-1595008718690-73' },
},
],
},
});
expect(getByText(/testImg\.jpg/)).toBeInTheDocument();
await waitFor(() => expect(getByText('File successfully uploaded.')).toBeVisible());
});
it('should render with Data and show error when upload mutation return error', async () => {
global.URL.createObjectURL = jest.fn();
const { getByLabelText, getByTestId, getByText } = render(
<MockedProvider
mocks={[
profileDetailsQueryMock.successCaptivePortal,
getAllProfilesQueryMock.success,
profileDetailsMutationMock.uploadError,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<ProfileDetails />
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByLabelText('Profile Name')).toHaveValue('Captive-portal'));
fireEvent.change(getByTestId('logoFile'), {
target: {
files: [
{
lastModified: 1595008730671,
lastModifiedDate: undefined,
name: 'testImg.jpg',
size: 100,
type: 'image/jpg',
percent: 0,
originFileObj: { uid: 'rc-upload-1595008718690-73' },
},
],
},
});
expect(getByText(/testImg\.jpg/)).toBeInTheDocument();
await waitFor(() => expect(getByText('File could not be uploaded.')).toBeVisible());
});
});

View File

@@ -0,0 +1,344 @@
import { GET_PROFILE } from 'graphql/queries';
import { FILE_UPLOAD, UPDATE_PROFILE } from 'graphql/mutations';
export const profileDetailsQueryMock = {
success: {
request: {
query: GET_PROFILE,
variables: { id: 123 },
},
result: {
data: {
getProfile: {
id: '123',
profileType: 'radius',
customerId: '2',
name: 'Radius-Profile',
childProfiles: [],
childProfileIds: [],
createdTimestamp: '0',
lastModifiedTimestamp: '1597243756957',
details: {
model_type: 'RadiusProfile',
subnetConfiguration: {
test: {
model_type: 'RadiusSubnetConfiguration',
subnetAddress: '0.0.0.0',
subnetCidrPrefix: 0,
subnetName: 'test',
proxyConfig: {
model_type: 'RadiusProxyConfiguration',
floatingIpAddress: null,
floatingIfCidrPrefix: null,
floatingIfGwAddress: null,
floatingIfVlan: null,
sharedSecret: null,
},
probeInterval: null,
serviceRegionName: 'Ottawa',
},
},
serviceRegionMap: {
Ottawa: {
model_type: 'RadiusServiceRegion',
serverMap: {
'Radius-Profile': [
{
model_type: 'RadiusServer',
ipAddress: '192.168.0.1',
secret: 'testing123',
authPort: 1812,
timeout: null,
},
],
},
regionName: 'Ottawa',
},
},
profileType: 'radius',
},
__typename: 'Profile',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
successCaptivePortal: {
request: {
query: GET_PROFILE,
variables: { id: 123 },
},
result: {
data: {
getProfile: {
id: '123',
profileType: 'captive_portal',
customerId: '2',
name: 'Captive-portal',
childProfiles: [],
childProfileIds: [],
createdTimestamp: '1597238768150',
lastModifiedTimestamp: '1597238768150',
details: {
model_type: 'CaptivePortalConfiguration',
name: 'Captive-portal',
browserTitle: 'Access the network as Guest',
headerContent: 'Captive Portal',
userAcceptancePolicy: 'Use this network at your own risk. No warranty of any kind.',
successPageMarkdownText: 'Welcome to the network',
redirectURL: '',
externalCaptivePortalURL: null,
sessionTimeoutInMinutes: 60,
logoFile: null,
backgroundFile: null,
walledGardenWhitelist: [],
usernamePasswordFile: null,
authenticationType: 'guest',
radiusAuthMethod: 'CHAP',
maxUsersWithSameCredentials: 42,
externalPolicyFile: null,
backgroundPosition: 'left_top',
backgroundRepeat: 'no_repeat',
radiusServiceName: null,
expiryType: 'unlimited',
userList: [],
macWhiteList: [],
profileType: 'captive_portal',
},
__typename: 'Profile',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_PROFILE,
variables: { id: 123 },
},
result: {
data: null,
errors: [{}],
loading: false,
networkStatus: 7,
stale: false,
},
},
};
export const profileDetailsMutationMock = {
updateSuccess: {
request: {
query: UPDATE_PROFILE,
variables: {
id: '123',
profileType: 'radius',
customerId: '2',
name: 'Radius-Profile',
childProfiles: [],
childProfileIds: [],
createdTimestamp: '0',
lastModifiedTimestamp: '1597243756957',
details: {
model_type: 'RadiusProfile',
subnetConfiguration: {
test: {
model_type: 'RadiusSubnetConfiguration',
subnetAddress: '0.0.0.0',
subnetCidrPrefix: 0,
subnetName: 'test',
proxyConfig: {
model_type: 'RadiusProxyConfiguration',
floatingIpAddress: null,
floatingIfCidrPrefix: null,
floatingIfGwAddress: null,
floatingIfVlan: null,
sharedSecret: null,
},
probeInterval: null,
serviceRegionName: 'Ottawa',
},
},
serviceRegionMap: {
Ottawa: {
regionName: 'Ottawa',
serverMap: {
'Radius-Profile': [
{
model_type: 'RadiusServer',
ipAddress: '192.168.0.1',
secret: 'testing123',
authPort: 1812,
timeout: null,
},
],
},
},
},
profileType: 'radius',
name: 'Radius-Profile',
probeInterval: 0,
services: [
{
name: 'Radius-Profile',
ips: [
{
model_type: 'RadiusServer',
ipAddress: '192.168.0.1',
secret: 'testing123',
authPort: 1812,
timeout: null,
},
],
},
],
zones: [
{
name: 'Ottawa',
subnets: [
{
model_type: 'RadiusSubnetConfiguration',
subnetAddress: '0.0.0.0',
subnetCidrPrefix: 0,
subnetName: 'test',
proxyConfig: {
model_type: 'RadiusProxyConfiguration',
floatingIpAddress: null,
floatingIfCidrPrefix: null,
floatingIfGwAddress: null,
floatingIfVlan: null,
sharedSecret: null,
},
probeInterval: null,
serviceRegionName: 'Ottawa',
},
],
},
],
},
},
},
result: {
data: {
updateProfile: {
id: '123',
profileType: 'radius',
customerId: '2',
name: 'Radius-Profile',
childProfileIds: [],
lastModifiedTimestamp: '1597245742733',
details: {
model_type: 'RadiusProfile',
subnetConfiguration: {
test: {
model_type: 'RadiusSubnetConfiguration',
subnetAddress: '0.0.0.0',
subnetCidrPrefix: 0,
subnetName: 'test',
proxyConfig: {
model_type: 'RadiusProxyConfiguration',
floatingIpAddress: null,
floatingIfCidrPrefix: null,
floatingIfGwAddress: null,
floatingIfVlan: null,
sharedSecret: null,
},
probeInterval: null,
serviceRegionName: 'Ottawa',
},
},
serviceRegionMap: {
Ottawa: {
model_type: 'RadiusServiceRegion',
serverMap: {
'Radius-Profile': [
{
model_type: 'RadiusServer',
ipAddress: '192.168.0.1',
secret: 'testing123',
authPort: 1812,
timeout: null,
},
],
},
regionName: 'Ottawa',
},
},
profileType: 'radius',
},
__typename: 'Profile',
},
},
},
},
updateError: {
request: {
query: UPDATE_PROFILE,
variables: {
id: 123,
username: 'test@test.com',
password: 'password',
role: 'CustomerIT',
customerId: '2',
lastModifiedTimestamp: '1597238767547',
},
},
result: {
data: null,
errors: [{}],
},
},
uploadSuccess: {
request: {
query: FILE_UPLOAD,
variables: {
fileName: 'testImg.jpg',
file: {
uid: '1234',
lastModified: 1595008730671,
lastModifiedDate: undefined,
name: 'testImg.jpg',
size: 100,
type: 'image/jpg',
percent: 0,
originFileObj: { uid: 'rc-upload-1595008718690-73' },
},
},
},
result: {
data: {
fileUpload: {
fileName: 'talkblog@4x.png',
baseUrl: 'https://localhost:9091/',
__typename: 'File',
},
},
},
},
uploadError: {
request: {
query: FILE_UPLOAD,
variables: {
fileName: 'testImg.jpg',
file: {
uid: '1234',
lastModified: 1595008730671,
lastModifiedDate: undefined,
name: 'testImg.jpg',
size: 100,
type: 'image/jpg',
percent: 0,
originFileObj: { uid: 'rc-upload-1595008718690-73' },
},
},
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -6,8 +6,9 @@ import { Alert, notification } from 'antd';
import { Profile as ProfilePage, Loading } from '@tip-wlan/wlan-cloud-ui-library';
import UserContext from 'contexts/UserContext';
import { DELETE_PROFILE } from 'graphql/mutations';
const GET_ALL_PROFILES = gql`
export const GET_ALL_PROFILES = gql`
query GetAllProfiles($customerId: ID!, $cursor: String, $limit: Int) {
getAllProfiles(customerId: $customerId, cursor: $cursor, limit: $limit) {
items {
@@ -25,14 +26,6 @@ const GET_ALL_PROFILES = gql`
}
`;
const DELETE_PROFILE = gql`
mutation DeleteProfile($id: ID!) {
deleteProfile(id: $id) {
id
}
}
`;
const Profiles = () => {
const { customerId } = useContext(UserContext);
const { loading, error, data, refetch, fetchMore } = useQuery(GET_ALL_PROFILES, {

View File

@@ -0,0 +1,171 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { BrowserRouter as Router } from 'react-router-dom';
import { screen } from '@testing-library/dom';
import UserProvider from 'contexts/UserProvider';
import { profilesMutationMock, profilesQueryMock } from './mock';
import Profiles from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<Profiles />', () => {
afterEach(jest.resetModules);
it('should render with Data', async () => {
const { getByText } = render(
<MockedProvider mocks={[profilesQueryMock.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
});
it('should show error when query return error', async () => {
const { getByText } = render(
<MockedProvider mocks={[profilesQueryMock.error]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load profiles.')).toBeVisible());
});
it('should render with Data and call onReload if reload button is clicked', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[profilesQueryMock.success, profilesQueryMock.success]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
fireEvent.click(getByRole('button', { name: /reload/i }));
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
});
it('should render with Data and show error if reload button is clicked', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[profilesQueryMock.success, profilesQueryMock.error]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
fireEvent.click(getByRole('button', { name: /reload/i }));
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
});
it('should render with Data and Load More Button Should show when isLastPage false', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[profilesQueryMock.success, profilesQueryMock.loadmore]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Load More' }));
await waitFor(() => expect(getByText('TipWlan-cloud-Enterprise')).toBeVisible());
});
it('should render with Data and Delete profile successfully', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
profilesQueryMock.success,
profilesQueryMock.loadmore,
profilesMutationMock.deleteSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Load More' }));
await waitFor(() => expect(getByText('TipWlan-cloud-Enterprise')).toBeVisible());
fireEvent.click(screen.getByTitle('delete'));
expect(getByRole('button', { name: 'Delete' }));
fireEvent.click(getByRole('button', { name: 'Delete' }));
await waitFor(() => expect(getByText('Profile successfully deleted.')).toBeVisible());
});
it('should render with Data and show error when Delete profile', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
profilesQueryMock.success,
profilesQueryMock.loadmore,
profilesMutationMock.deleteError,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Profiles />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Radius-Profile')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Load More' }));
await waitFor(() => expect(getByText('TipWlan-cloud-Enterprise')).toBeVisible());
fireEvent.click(screen.getByTitle('delete'));
expect(getByRole('button', { name: 'Delete' }));
fireEvent.click(getByRole('button', { name: 'Delete' }));
await waitFor(() => expect(getByText('Profile could not be deleted.')).toBeVisible());
});
});

View File

@@ -0,0 +1,175 @@
import { DELETE_PROFILE } from 'graphql/mutations';
import { GET_ALL_PROFILES } from '..';
export const profilesQueryMock = {
success: {
request: {
query: GET_ALL_PROFILES,
variables: { customerId: 2, limit: 100 },
},
result: {
data: {
getAllProfiles: {
items: [
{
id: '123',
name: 'Radius-Profile',
profileType: 'radius',
details: {
model_type: 'RadiusProfile',
subnetConfiguration: {
test: {
model_type: 'RadiusSubnetConfiguration',
subnetAddress: '0.0.0.0',
subnetCidrPrefix: 0,
subnetName: 'test',
proxyConfig: {
model_type: 'RadiusProxyConfiguration',
floatingIpAddress: null,
floatingIfCidrPrefix: null,
floatingIfGwAddress: null,
floatingIfVlan: null,
sharedSecret: null,
},
probeInterval: null,
serviceRegionName: 'Ottawa',
},
},
serviceRegionMap: {
Ottawa: {
model_type: 'RadiusServiceRegion',
serverMap: {
'Radius-Profile': [
{
model_type: 'RadiusServer',
ipAddress: '192.168.0.1',
secret: 'testing123',
authPort: 1812,
timeout: null,
},
],
},
regionName: 'Ottawa',
},
},
profileType: 'radius',
},
__typename: 'Profile',
},
],
context: {
cursor: 'test',
lastPage: false,
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
loadmore: {
request: {
query: GET_ALL_PROFILES,
variables: { customerId: 2, limit: 100, cursor: 'test' },
},
result: {
data: {
getAllProfiles: {
items: [
{
id: '2',
name: 'TipWlan-cloud-Enterprise',
profileType: 'ssid',
details: {
model_type: 'SsidConfiguration',
ssid: 'Default-SSID-1597238767760',
appliedRadios: ['is5GHzU', 'is2dot4GHz', 'is5GHzL'],
ssidAdminState: 'enabled',
secureMode: 'wpaEAP',
vlanId: 1,
keyStr: 'testing123',
broadcastSsid: 'enabled',
keyRefresh: 0,
noLocalSubnets: false,
radiusServiceName: 'Radius-Profile',
captivePortalId: null,
bandwidthLimitDown: 0,
bandwidthLimitUp: 0,
videoTrafficOnly: false,
radioBasedConfigs: {
is5GHz: {
model_type: 'RadioBasedSsidConfiguration',
enable80211r: null,
enable80211k: null,
enable80211v: null,
},
is2dot4GHz: {
model_type: 'RadioBasedSsidConfiguration',
enable80211r: null,
enable80211k: null,
enable80211v: null,
},
is5GHzU: {
model_type: 'RadioBasedSsidConfiguration',
enable80211r: null,
enable80211k: null,
enable80211v: null,
},
is5GHzL: {
model_type: 'RadioBasedSsidConfiguration',
enable80211r: null,
enable80211k: null,
enable80211v: null,
},
},
bonjourGatewayProfileId: null,
enable80211w: null,
wepConfig: null,
forwardMode: null,
profileType: 'ssid',
},
},
],
context: {
cursor: 'test2',
lastPage: true,
},
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_ALL_PROFILES,
variables: { customerId: 2 },
},
result: {
data: null,
errors: [{}],
},
},
};
export const profilesMutationMock = {
deleteSuccess: {
request: {
query: DELETE_PROFILE,
variables: { id: '2' },
},
result: { data: { deleteProfile: { id: '2', __typename: 'Profile' } } },
},
deleteError: {
request: {
query: DELETE_PROFILE,
variables: { id: '2' },
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -0,0 +1,124 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { BrowserRouter as Router } from 'react-router-dom';
import UserProvider from 'contexts/UserProvider';
import { autoProvisionMutationMock, autoProvisionQueryMock } from './mock';
import AutoProvision from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<AutoProvision />', () => {
afterEach(jest.resetModules);
it('should render with Data', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
autoProvisionQueryMock.getAllProfilesSuccess,
autoProvisionQueryMock.getCustomerSuccess,
autoProvisionQueryMock.getAllLocationsSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<AutoProvision />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('EA8300')).toBeVisible());
});
it('should show error when customer query return error', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
autoProvisionQueryMock.getAllProfilesSuccess,
autoProvisionQueryMock.error,
autoProvisionQueryMock.getAllLocationsSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<AutoProvision />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load Customer Data.')).toBeVisible());
});
it('should render with Data and update successfully', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
autoProvisionQueryMock.getAllProfilesSuccess,
autoProvisionQueryMock.getCustomerSuccess,
autoProvisionQueryMock.getCustomerSuccess,
autoProvisionQueryMock.getAllLocationsSuccess,
autoProvisionMutationMock.success,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<AutoProvision />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('EA8300')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Settings successfully updated.')).toBeVisible());
await waitFor(() => expect(getByText('EA8300')).toBeVisible());
});
it('should render with Data and update show error when mutation return error', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
autoProvisionQueryMock.getAllProfilesSuccess,
autoProvisionQueryMock.getCustomerSuccess,
autoProvisionQueryMock.getCustomerSuccess,
autoProvisionQueryMock.getAllLocationsSuccess,
autoProvisionMutationMock.error,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<AutoProvision />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('EA8300')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Settings could not be updated.')).toBeVisible());
});
});

View File

@@ -0,0 +1,361 @@
import { GET_ALL_LOCATIONS, GET_ALL_PROFILES, GET_CUSTOMER } from 'graphql/queries';
import { UPDATE_CUSTOMER } from 'graphql/mutations';
export const autoProvisionQueryMock = {
getAllProfilesSuccess: {
request: {
query: GET_ALL_PROFILES,
variables: { customerId: 2, type: 'equipment_ap' },
},
result: {
data: {
getAllProfiles: {
items: [
{
id: '6',
name: 'ApProfile-3-radios',
profileType: 'equipment_ap',
details: {
model_type: 'ApNetworkConfiguration',
networkConfigVersion: 'AP-1',
equipmentType: 'AP',
vlanNative: true,
vlan: 0,
ntpServer: {
model_type: 'AutoOrManualString',
auto: true,
value: 'pool.ntp.org',
},
syslogRelay: null,
rtlsSettings: null,
syntheticClientEnabled: true,
ledControlEnabled: true,
equipmentDiscovery: false,
radioMap: {
is2dot4GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is5GHzU: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is5GHzL: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
},
profileType: 'equipment_ap',
},
__typename: 'Profile',
},
{
id: '7',
name: 'ApProfile-2-radios',
profileType: 'equipment_ap',
details: {
model_type: 'ApNetworkConfiguration',
networkConfigVersion: 'AP-1',
equipmentType: 'AP',
vlanNative: true,
vlan: 0,
ntpServer: {
model_type: 'AutoOrManualString',
auto: true,
value: 'pool.ntp.org',
},
syslogRelay: null,
rtlsSettings: null,
syntheticClientEnabled: true,
ledControlEnabled: true,
equipmentDiscovery: false,
radioMap: {
is5GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is2dot4GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
},
profileType: 'equipment_ap',
},
__typename: 'Profile',
},
{
id: '8',
name: 'EnterpriseApProfile',
profileType: 'equipment_ap',
details: {
model_type: 'ApNetworkConfiguration',
networkConfigVersion: 'AP-1',
equipmentType: 'AP',
vlanNative: true,
vlan: 0,
ntpServer: {
model_type: 'AutoOrManualString',
auto: true,
value: 'pool.ntp.org',
},
syslogRelay: null,
rtlsSettings: null,
syntheticClientEnabled: true,
ledControlEnabled: true,
equipmentDiscovery: false,
radioMap: {
is5GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is2dot4GHz: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is5GHzU: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
is5GHzL: {
model_type: 'RadioProfileConfiguration',
bestApEnabled: true,
bestAPSteerType: 'both',
},
},
profileType: 'equipment_ap',
},
__typename: 'Profile',
},
],
context: {
cursor:
'bnVsbEBAQHsibW9kZWxfdHlwZSI6IkNvbnRleHRDaGlsZHJlbiIsImNoaWxkcmVuIjp7fX1AQEBudWxs',
lastPage: true,
__typename: 'PaginationContext',
},
__typename: 'ProfilePagination',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
getCustomerSuccess: {
request: {
query: GET_CUSTOMER,
variables: { id: 2 },
},
result: {
data: {
getCustomer: {
id: '2',
name: 'Test Customer',
email: 'test@example.com',
createdTimestamp: '1597329016138',
lastModifiedTimestamp: '1597329017058',
details: {
model_type: 'CustomerDetails',
autoProvisioning: {
model_type: 'EquipmentAutoProvisioningSettings',
enabled: true,
locationId: 8,
equipmentProfileIdPerModel: {
default: 6,
TIP_AP: 7,
ECW5410: 7,
ECW5211: 7,
AP2220: 7,
'EA8300-CA': 6,
EA8300: 6,
},
},
},
__typename: 'Customer',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
getAllLocationsSuccess: {
request: {
query: GET_ALL_LOCATIONS,
variables: { customerId: 2 },
},
result: {
data: {
getAllLocations: [
{
id: '2',
name: 'Menlo Park',
parentId: '0',
locationType: 'SITE',
__typename: 'Location',
},
{
id: '3',
name: 'Building 1',
parentId: '2',
locationType: 'BUILDING',
__typename: 'Location',
},
{
id: '4',
name: 'Floor 1',
parentId: '3',
locationType: 'FLOOR',
__typename: 'Location',
},
{
id: '5',
name: 'Floor 2',
parentId: '3',
locationType: 'FLOOR',
__typename: 'Location',
},
{
id: '6',
name: 'Floor 3',
parentId: '3',
locationType: 'FLOOR',
__typename: 'Location',
},
{
id: '7',
name: 'Building 2',
parentId: '2',
locationType: 'BUILDING',
__typename: 'Location',
},
{
id: '8',
name: 'Ottawa',
parentId: '0',
locationType: 'SITE',
__typename: 'Location',
},
],
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_CUSTOMER,
variables: { id: 2 },
},
result: {
data: null,
errors: [{}],
},
},
};
export const autoProvisionMutationMock = {
success: {
request: {
query: UPDATE_CUSTOMER,
variables: {
id: '2',
email: 'test@example.com',
name: 'Test Customer',
details: {
model_type: 'CustomerDetails',
autoProvisioning: {
model_type: 'EquipmentAutoProvisioningSettings',
enabled: true,
locationId: '8',
equipmentProfileIdPerModel: {
default: 6,
TIP_AP: 7,
ECW5410: 7,
ECW5211: 7,
AP2220: 7,
'EA8300-CA': 6,
EA8300: 6,
},
},
},
createdTimestamp: '1597329016138',
lastModifiedTimestamp: '1597329017058',
},
},
result: {
data: {
updateCustomer: {
id: '2',
email: 'test@example.com',
name: 'Test Customer',
details: {
model_type: 'CustomerDetails',
autoProvisioning: {
model_type: 'EquipmentAutoProvisioningSettings',
enabled: true,
locationId: 8,
equipmentProfileIdPerModel: {
default: 6,
TIP_AP: 7,
ECW5410: 7,
ECW5211: 7,
AP2220: 7,
'EA8300-CA': 6,
EA8300: 6,
},
},
},
createdTimestamp: '1597329016138',
lastModifiedTimestamp: '1597331154979',
__typename: 'Customer',
},
},
},
},
error: {
request: {
query: UPDATE_CUSTOMER,
variables: {
id: '2',
email: 'test@example.com',
name: 'Test Customer',
details: {
model_type: 'CustomerDetails',
autoProvisioning: {
model_type: 'EquipmentAutoProvisioningSettings',
enabled: true,
locationId: '8',
equipmentProfileIdPerModel: {
default: 6,
TIP_AP: 7,
ECW5410: 7,
ECW5211: 7,
AP2220: 7,
'EA8300-CA': 6,
EA8300: 6,
},
},
},
createdTimestamp: '1597329016138',
lastModifiedTimestamp: '1597329017058',
},
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -0,0 +1,167 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { BrowserRouter as Router } from 'react-router-dom';
import UserProvider from 'contexts/UserProvider';
import { blockListMutationMock, blockListQueryMock } from './mock';
import BlockedList from '..';
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<BlockedList />', () => {
afterEach(jest.resetModules);
it('should render with Data', async () => {
const { getByText } = render(
<MockedProvider mocks={[blockListQueryMock.success]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<BlockedList />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
});
it('should show error when Block query return error', async () => {
const { getByText } = render(
<MockedProvider mocks={[blockListQueryMock.error]} addTypename={false}>
<UserProvider {...mockProp}>
<Router>
<BlockedList />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('Failed to load Client Data.')).toBeVisible());
});
it('should render with Data and add Client successfully', async () => {
const { getByText, getByLabelText, getByRole } = render(
<MockedProvider
mocks={[
blockListQueryMock.success,
blockListQueryMock.success,
blockListMutationMock.addClientSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<BlockedList />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Add Client' }));
fireEvent.change(getByLabelText('MAC Address'), { target: { value: '74:8c:00:01:45:ae' } });
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() =>
expect(getByText('Client successfully added to Blocked List')).toBeVisible()
);
});
it('should render with Data and add Client return error when mutation not work', async () => {
const { getByText, getByLabelText, getByRole } = render(
<MockedProvider
mocks={[blockListQueryMock.success, blockListMutationMock.addClientError]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<BlockedList />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
fireEvent.click(getByRole('button', { name: 'Add Client' }));
fireEvent.change(getByLabelText('MAC Address'), { target: { value: '74:8c:00:01:45:ae' } });
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() =>
expect(getByText('Client could not be added to Blocked List')).toBeVisible()
);
});
it('should render with Data and update Client successfully', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
blockListQueryMock.success,
blockListQueryMock.success,
blockListMutationMock.updateClientSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<BlockedList />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
fireEvent.click(
getByRole('button', {
name: `delete-mac-74:9c:00:01:45:ae`,
})
);
fireEvent.click(getByRole('button', { name: 'Remove' }));
await waitFor(() =>
expect(getByText('Client successfully removed from Blocked List')).toBeVisible()
);
});
it('should render with Data and update Client return error when mutation not work', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
blockListQueryMock.success,
blockListQueryMock.success,
blockListMutationMock.updateClientError,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<BlockedList />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('74:9c:00:01:45:ae')).toBeVisible());
fireEvent.click(
getByRole('button', {
name: `delete-mac-74:9c:00:01:45:ae`,
})
);
fireEvent.click(getByRole('button', { name: 'Remove' }));
await waitFor(() =>
expect(getByText('Client could not be removed from Blocked List')).toBeVisible()
);
});
});

View File

@@ -0,0 +1,189 @@
import { ADD_BLOCKED_CLIENT, UPDATE_CLIENT } from 'graphql/mutations';
import { GET_BLOCKED_CLIENTS } from 'graphql/queries';
export const blockListQueryMock = {
success: {
request: {
query: GET_BLOCKED_CLIENTS,
variables: { customerId: 2 },
},
result: {
data: {
getBlockedClients: [
{
customerId: '2',
macAddress: '74:9c:00:01:45:ae',
createdTimestamp: '1597172802575',
lastModifiedTimestamp: '1597333913133',
details: {
model_type: 'ClientInfoDetails',
alias: 'alias 128213363803566',
clientType: 0,
apFingerprint: 'fp 74:9c:00:01:45:ae',
userName: 'user-128213363803566',
hostName: 'hostName-128213363803566',
lastUsedCpUsername: null,
lastUserAgent: null,
doNotSteer: false,
blocklistDetails: {
model_type: 'BlocklistDetails',
enabled: true,
startTime: null,
endTime: null,
},
},
__typename: 'Client',
},
],
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_BLOCKED_CLIENTS,
variables: { customerId: 2 },
},
result: {
data: null,
errors: [{}],
},
},
};
export const blockListMutationMock = {
addClientSuccess: {
request: {
query: ADD_BLOCKED_CLIENT,
variables: {
customerId: 2,
macAddress: '74:8c:00:01:45:ae',
},
},
result: {
data: {
addBlockedClient: {
customerId: '2',
macAddress: '74:8c:00:01:45:ae',
details: {
model_type: 'ClientInfoDetails',
alias: null,
clientType: 0,
apFingerprint: null,
userName: null,
hostName: null,
lastUsedCpUsername: null,
lastUserAgent: null,
doNotSteer: false,
blocklistDetails: {
model_type: 'BlocklistDetails',
enabled: true,
startTime: null,
endTime: null,
},
},
lastModifiedTimestamp: '1597334825157',
createdTimestamp: '1597334825157',
__typename: 'Client',
},
},
},
},
addClientError: {
request: {
query: ADD_BLOCKED_CLIENT,
},
result: {
data: null,
errors: [{}],
},
},
updateClientSuccess: {
request: {
query: UPDATE_CLIENT,
variables: {
customerId: 2,
macAddress: '74:9c:00:01:45:ae',
details: {
model_type: 'ClientInfoDetails',
alias: 'alias 128213363803566',
clientType: 0,
apFingerprint: 'fp 74:9c:00:01:45:ae',
userName: 'user-128213363803566',
hostName: 'hostName-128213363803566',
lastUsedCpUsername: null,
lastUserAgent: null,
doNotSteer: false,
blocklistDetails: {
model_type: 'BlocklistDetails',
enabled: false,
startTime: null,
endTime: null,
},
},
},
},
result: {
data: {
updateClient: {
customerId: '2',
macAddress: 'mac-74:9c:00:01:45:ae',
createdTimestamp: '0',
lastModifiedTimestamp: '1597336036972',
details: {
model_type: 'ClientInfoDetails',
alias: 'alias 128213363803566',
clientType: 0,
apFingerprint: 'fp 74:9c:00:01:45:ae',
userName: 'user-128213363803566',
hostName: 'hostName-128213363803566',
lastUsedCpUsername: null,
lastUserAgent: null,
doNotSteer: false,
blocklistDetails: {
model_type: 'BlocklistDetails',
enabled: false,
startTime: null,
endTime: null,
},
},
__typename: 'Client',
},
},
},
},
updateClientError: {
request: {
query: UPDATE_CLIENT,
variables: {
customerId: 2,
macAddress: '74:9c:00:01:45:ae',
details: {
model_type: 'ClientInfoDetails',
alias: 'alias 128213363803566',
clientType: 0,
apFingerprint: 'fp 74:9c:00:01:45:ae',
userName: 'user-128213363803566',
hostName: 'hostName-128213363803566',
lastUsedCpUsername: null,
lastUserAgent: null,
doNotSteer: false,
blocklistDetails: {
model_type: 'BlocklistDetails',
enabled: false,
startTime: null,
endTime: null,
},
},
},
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -0,0 +1,119 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { BrowserRouter as Router } from 'react-router-dom';
import UserProvider from 'contexts/UserProvider';
import { firmwareMutationMock, firmwareQueryMock } from './mock';
import Firmware from '..';
const {
getAllFirmwareModelsSuccess,
getAllFirmwareSuccess,
getFirmwareTrackAssignmentSuccess,
getFirmwareTrackSuccess,
} = firmwareQueryMock;
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
const mockProp = {
id: 123,
email: 'test@test.com',
role: 'admin',
customerId: 2,
updateUser: jest.fn(),
updateToken: jest.fn(),
};
describe('<Firmware />', () => {
afterEach(jest.resetModules);
it('should render with Data', async () => {
const { getByText } = render(
<MockedProvider
mocks={[
getAllFirmwareModelsSuccess,
getAllFirmwareSuccess,
getFirmwareTrackAssignmentSuccess,
getFirmwareTrackSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Firmware />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('ap2220-2020-06-25-ce03472')).toBeVisible());
});
it('should render with Data and Edit Model Target successfully', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
getAllFirmwareModelsSuccess,
getAllFirmwareSuccess,
getFirmwareTrackAssignmentSuccess,
getFirmwareTrackAssignmentSuccess,
getFirmwareTrackSuccess,
firmwareMutationMock.updateTrackAssignmentSuccess,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Firmware />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('ap2220-2020-06-25-ce03472')).toBeVisible());
fireEvent.click(getByRole('button', { name: `edit-track-ap2220` }));
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() =>
expect(getByText('Model Target Version successfully updated.')).toBeVisible()
);
});
it('should render with Data and Edit Model Target show error when mutation not work ', async () => {
const { getByText, getByRole } = render(
<MockedProvider
mocks={[
getAllFirmwareModelsSuccess,
getAllFirmwareSuccess,
getFirmwareTrackAssignmentSuccess,
getFirmwareTrackAssignmentSuccess,
getFirmwareTrackSuccess,
firmwareMutationMock.updateTrackAssignmentError,
]}
addTypename={false}
>
<UserProvider {...mockProp}>
<Router>
<Firmware />
</Router>
</UserProvider>
</MockedProvider>
);
await waitFor(() => expect(getByText('ap2220-2020-06-25-ce03472')).toBeVisible());
fireEvent.click(getByRole('button', { name: `edit-track-ap2220` }));
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() =>
expect(getByText('Model Target Version could not be updated.')).toBeVisible()
);
});
});

View File

@@ -0,0 +1,223 @@
import { UPDATE_TRACK_ASSIGNMENT } from 'graphql/mutations';
import {
GET_ALL_FIRMWARE,
GET_ALL_FIRMWARE_MODELS,
GET_FIRMWARE_TRACK,
GET_TRACK_ASSIGNMENTS,
} from 'graphql/queries';
export const firmwareQueryMock = {
getAllFirmwareSuccess: {
request: {
query: GET_ALL_FIRMWARE,
},
result: {
data: {
getAllFirmware: [
{
id: '1',
modelId: 'ap2220',
versionName: 'ap2220-2020-06-25-ce03472',
description: '',
filename:
'https://tip-read:tip-read@tip.jfrog.io/artifactory/tip-wlan-ap-firmware/ap2220/ap2220-2020-06-25-ce03472.tar.gz',
commit: 'ce03472',
releaseDate: '1597329017106',
validationCode: 'c69370aa5b6622d91a0fba3a5441f31c',
createdTimestamp: '1597329017115',
lastModifiedTimestamp: '1597329017115',
__typename: 'Firmware',
},
{
id: '2',
modelId: 'ea8300',
versionName: 'ea8300-2020-06-25-ce03472',
description: '',
filename:
'https://tip-read:tip-read@tip-read:tip-read@tip.jfrog.io/artifactory/tip-wlan-ap-firmware/ea8300/ea8300-2020-06-25-ce03472.tar.gz',
commit: 'ce03472',
releaseDate: '1597329017115',
validationCode: 'b209deb9847bdf40a31e45edf2e5a8d7',
createdTimestamp: '1597329017115',
lastModifiedTimestamp: '1597329017115',
__typename: 'Firmware',
},
{
id: '3',
modelId: 'ea8300-ca',
versionName: 'ea8300-2020-06-25-ce03472',
description: '',
filename:
'https://tip-read:tip-read@tip.jfrog.io/artifactory/tip-wlan-ap-firmware/ea8300/ea8300-2020-06-25-ce03472.tar.gz',
commit: 'ce03472',
releaseDate: '1597329017115',
validationCode: 'b209deb9847bdf40a31e45edf2e5a8d7',
createdTimestamp: '1597329017115',
lastModifiedTimestamp: '1597329017115',
__typename: 'Firmware',
},
{
id: '4',
modelId: 'ecw5211',
versionName: 'ecw5211-2020-06-26-4ff7208',
description: '',
filename:
'https://tip-read:tip-read@tip.jfrog.io/artifactory/tip-wlan-ap-firmware/ecw5211/ecw5211-2020-06-26-4ff7208.tar.gz',
commit: '4ff7208',
releaseDate: '1597329017115',
validationCode: '133072b0e8a440063109604375938fba',
createdTimestamp: '1597329017115',
lastModifiedTimestamp: '1597329017115',
__typename: 'Firmware',
},
{
id: '5',
modelId: 'ecw5410',
versionName: 'ecw5410-2020-06-25-ce03472',
description: '',
filename:
'https://tip-read:tip-read@tip.jfrog.io/artifactory/tip-wlan-ap-firmware/ecw5410/ecw5410-2020-06-25-ce03472.tar.gz',
commit: 'ce03472',
releaseDate: '1597329017115',
validationCode: '2940ca34eeab85be18f3a4b79f4da6d9',
createdTimestamp: '1597329017115',
lastModifiedTimestamp: '1597329017115',
__typename: 'Firmware',
},
],
},
loading: false,
networkStatus: 7,
stale: false,
},
},
getAllFirmwareModelsSuccess: {
request: {
query: GET_ALL_FIRMWARE_MODELS,
},
result: {
data: {
getAllFirmwareModelId: ['ea8300-ca', 'ap2220', 'ecw5211', 'ea8300', 'ecw5410'],
},
loading: false,
networkStatus: 7,
stale: false,
},
},
getFirmwareTrackAssignmentSuccess: {
request: {
query: GET_TRACK_ASSIGNMENTS,
},
result: {
data: {
getAllFirmwareTrackAssignment: [
{
modelId: 'ap2220',
firmwareVersionRecordId: '1',
trackRecordId: '1',
lastModifiedTimestamp: '1597342689340',
__typename: 'FirmwareTrackAssignment',
},
{
modelId: 'ea8300',
firmwareVersionRecordId: '2',
trackRecordId: '1',
lastModifiedTimestamp: '1597172802693',
__typename: 'FirmwareTrackAssignment',
},
{
modelId: 'ea8300-ca',
firmwareVersionRecordId: '3',
trackRecordId: '1',
lastModifiedTimestamp: '1597172802693',
__typename: 'FirmwareTrackAssignment',
},
{
modelId: 'ecw5211',
firmwareVersionRecordId: '4',
trackRecordId: '1',
lastModifiedTimestamp: '1597172802693',
__typename: 'FirmwareTrackAssignment',
},
],
},
loading: false,
networkStatus: 7,
stale: false,
},
},
getFirmwareTrackSuccess: {
request: {
query: GET_FIRMWARE_TRACK,
variables: { firmwareTrackName: 'DEFAULT' },
},
result: {
data: {
getFirmwareTrack: {
recordId: '1',
trackName: 'DEFAULT',
createdTimestamp: '1597329017117',
lastModifiedTimestamp: '1597329017117',
__typename: 'FirmwareTrack',
},
},
loading: false,
networkStatus: 7,
stale: false,
},
},
error: {
request: {
query: GET_FIRMWARE_TRACK,
variables: { customerId: 2 },
},
result: {
data: null,
errors: [{}],
},
},
};
export const firmwareMutationMock = {
updateTrackAssignmentSuccess: {
request: {
query: UPDATE_TRACK_ASSIGNMENT,
variables: {
trackRecordId: '1',
firmwareVersionRecordId: '1',
modelId: 'ap2220',
lastModifiedTimestamp: '1597342689340',
},
},
result: {
data: {
updateFirmwareTrackAssignment: {
trackRecordId: '1',
firmwareVersionRecordId: '1',
modelId: 'ap2220',
createdTimestamp: '0',
lastModifiedTimestamp: '1597342689340',
__typename: 'FirmwareTrackAssignment',
},
},
},
},
updateTrackAssignmentError: {
request: {
query: UPDATE_TRACK_ASSIGNMENT,
variables: {
trackRecordId: '1',
firmwareVersionRecordId: '1',
modelId: 'ap2220',
lastModifiedTimestamp: '1597342689340',
},
},
result: {
data: null,
errors: [{}],
},
},
};

View File

@@ -10,6 +10,167 @@ export const REFRESH_TOKEN = gql`
}
`;
export const AUTHENTICATE_USER = gql`
mutation AuthenticateUser($email: String!, $password: String!) {
authenticateUser(email: $email, password: $password) {
access_token
refresh_token
expires_in
}
}
`;
export const UPDATE_PROFILE = gql`
mutation UpdateProfile(
$id: ID!
$profileType: String!
$customerId: ID!
$name: String!
$childProfileIds: [ID]
$lastModifiedTimestamp: String
$details: JSONObject
) {
updateProfile(
id: $id
profileType: $profileType
customerId: $customerId
name: $name
childProfileIds: $childProfileIds
lastModifiedTimestamp: $lastModifiedTimestamp
details: $details
) {
id
profileType
customerId
name
childProfileIds
lastModifiedTimestamp
details
}
}
`;
export const UPDATE_USER = gql`
mutation UpdateUser(
$id: ID!
$username: String!
$password: String!
$role: String!
$customerId: ID!
$lastModifiedTimestamp: String
) {
updateUser(
id: $id
username: $username
password: $password
role: $role
customerId: $customerId
lastModifiedTimestamp: $lastModifiedTimestamp
) {
id
username
role
customerId
lastModifiedTimestamp
}
}
`;
export const UPDATE_EQUIPMENT = gql`
mutation UpdateEquipment(
$id: ID!
$equipmentType: String!
$inventoryId: String!
$customerId: ID!
$profileId: ID!
$locationId: ID!
$name: String!
$latitude: String
$longitude: String
$serial: String
$lastModifiedTimestamp: String
$details: JSONObject
) {
updateEquipment(
id: $id
equipmentType: $equipmentType
inventoryId: $inventoryId
customerId: $customerId
profileId: $profileId
locationId: $locationId
name: $name
latitude: $latitude
longitude: $longitude
serial: $serial
lastModifiedTimestamp: $lastModifiedTimestamp
details: $details
) {
id
equipmentType
inventoryId
customerId
profileId
locationId
name
latitude
longitude
serial
lastModifiedTimestamp
details
}
}
`;
export const DELETE_PROFILE = gql`
mutation DeleteProfile($id: ID!) {
deleteProfile(id: $id) {
id
}
}
`;
export const CREATE_PROFILE = gql`
mutation CreateProfile(
$profileType: String!
$customerId: ID!
$name: String!
$childProfileIds: [ID]
$details: JSONObject
) {
createProfile(
profileType: $profileType
customerId: $customerId
name: $name
childProfileIds: $childProfileIds
details: $details
) {
profileType
customerId
name
childProfileIds
details
}
}
`;
export const CREATE_USER = gql`
mutation CreateUser($username: String!, $password: String!, $role: String!, $customerId: ID!) {
createUser(username: $username, password: $password, role: $role, customerId: $customerId) {
username
role
customerId
}
}
`;
export const DELETE_USER = gql`
mutation DeleteUser($id: ID!) {
deleteUser(id: $id) {
id
}
}
`;
export const CREATE_LOCATION = gql`
mutation CreateLocation(
$locationType: String!

View File

@@ -1,5 +1,171 @@
import gql from 'graphql-tag';
export const GET_PROFILE = gql`
query GetProfile($id: ID!) {
getProfile(id: $id) {
id
profileType
customerId
name
childProfiles {
id
name
profileType
details
}
childProfileIds
createdTimestamp
lastModifiedTimestamp
details
}
}
`;
export const GET_USER = gql`
query GetUser($id: ID!) {
getUser(id: $id) {
id
username
role
customerId
lastModifiedTimestamp
}
}
`;
export const GET_EQUIPMENT = gql`
query GetEquipment($id: ID!) {
getEquipment(id: $id) {
id
equipmentType
inventoryId
customerId
profileId
locationId
name
latitude
longitude
serial
lastModifiedTimestamp
details
profile {
id
name
childProfiles {
id
name
details
}
}
status {
firmware {
detailsJSON
}
protocol {
detailsJSON
details {
reportedMacAddr
manufacturer
}
}
radioUtilization {
detailsJSON
}
clientDetails {
detailsJSON
details {
numClientsPerRadio
}
}
osPerformance {
detailsJSON
}
}
model
alarmsCount
alarms {
severity
alarmCode
details
createdTimestamp
}
}
}
`;
export const GET_ALL_FIRMWARE = gql`
query GetAllFirmware($modelId: String) {
getAllFirmware(modelId: $modelId) {
id
modelId
versionName
description
filename
commit
releaseDate
validationCode
createdTimestamp
lastModifiedTimestamp
}
}
`;
export const GET_ALL_PROFILES = gql`
query GetAllProfiles($customerId: ID!, $cursor: String, $type: String) {
getAllProfiles(customerId: $customerId, cursor: $cursor, type: $type) {
items {
id
name
profileType
details
}
context {
cursor
lastPage
}
}
}
`;
export const GET_ALL_ALARMS = gql`
query GetAllAlarms($customerId: ID!, $cursor: String) {
getAllAlarms(customerId: $customerId, cursor: $cursor) {
items {
severity
alarmCode
details
createdTimestamp
equipment {
id
name
}
}
context {
cursor
lastPage
}
}
}
`;
export const GET_ALL_USERS = gql`
query GetAllUsers($customerId: ID!, $cursor: String) {
getAllUsers(customerId: $customerId, cursor: $cursor) {
items {
id
email: username
role
lastModifiedTimestamp
customerId
}
context {
cursor
lastPage
}
}
}
`;
export const GET_ALL_LOCATIONS = gql`
query GetAllLocations($customerId: ID!) {
getAllLocations(customerId: $customerId) {
@@ -191,23 +357,6 @@ export const FILTER_SERVICE_METRICS = gql`
}
`;
export const GET_ALL_PROFILES = gql`
query GetAllProfiles($customerId: ID!, $cursor: String, $type: String) {
getAllProfiles(customerId: $customerId, cursor: $cursor, type: $type) {
items {
id
name
profileType
details
}
context {
cursor
lastPage
}
}
}
`;
export const GET_ALL_STATUS = gql`
query GetAllStatus($customerId: ID!, $statusDataTypes: [String]) {
getAllStatus(customerId: $customerId, statusDataTypes: $statusDataTypes) {
@@ -244,23 +393,6 @@ export const GET_FIRMWARE_TRACK = gql`
}
`;
export const GET_ALL_FIRMWARE = gql`
query GetAllFirmware($modelId: String) {
getAllFirmware(modelId: $modelId) {
id
modelId
versionName
description
filename
commit
releaseDate
validationCode
createdTimestamp
lastModifiedTimestamp
}
}
`;
export const GET_TRACK_ASSIGNMENTS = gql`
query GetAllFirmwareTrackAssignment {
getAllFirmwareTrackAssignment {

469
package-lock.json generated
View File

@@ -105,6 +105,17 @@
"tslib": "^1.10.0"
}
},
"@apollo/react-testing": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@apollo/react-testing/-/react-testing-3.0.0.tgz",
"integrity": "sha512-AUhjWdIx3hgG/rwvg4Bf3GPw61BReh0feSO2Xa4mh9T+CqcKRb0OwmIwAdh/C0BoPf3zUKROBNd9IJhs80LPFA==",
"dev": true,
"requires": {
"@apollo/react-common": "^3.0.0",
"fast-json-stable-stringify": "^2.0.0",
"tslib": "^1.10.0"
}
},
"@babel/code-frame": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
@@ -1850,16 +1861,159 @@
}
},
"@testing-library/dom": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.2.2.tgz",
"integrity": "sha512-g+gT//COYh2FgRrlgcgdkifkjqSk7wQIS7F8jbrf6yoEsh85PJUJ/QtO0bJ9QU7pQPYQgKcgqNJsOs0dlyFYag==",
"version": "7.22.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.22.1.tgz",
"integrity": "sha512-bEszhvj9LcspaRz56mqGV7uc+vJTAYKCKPJcGb5X6U1qBysgTAgCexQXvKZ3BBjWu5S/TANP2NniOVsMWqKXcw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.3",
"@types/aria-query": "^4.2.0",
"aria-query": "^4.2.2",
"dom-accessibility-api": "^0.5.0",
"pretty-format": "^25.5.0"
},
"dependencies": {
"@babel/runtime": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
}
}
},
"@testing-library/jest-dom": {
"version": "5.11.2",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.2.tgz",
"integrity": "sha512-s+rWJx+lanEGKqvOl4qJR0rGjCrxsEjj9qjxFlg4NV4/FRD7fnUUAWPHqwpyafNHfLYArs58FADgdn4UKmjFmw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.2",
"@types/testing-library__dom": "^7.0.0",
"aria-query": "^4.0.2",
"dom-accessibility-api": "^0.4.2",
"pretty-format": "^25.1.0"
"@types/testing-library__jest-dom": "^5.9.1",
"aria-query": "^4.2.2",
"chalk": "^3.0.0",
"css": "^3.0.0",
"css.escape": "^1.5.1",
"jest-diff": "^25.1.0",
"jest-matcher-utils": "^25.1.0",
"lodash": "^4.17.15",
"redent": "^3.0.0"
},
"dependencies": {
"@babel/runtime-corejs3": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz",
"integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==",
"dev": true,
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.4"
}
},
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
"integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
},
"dependencies": {
"@babel/runtime": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
}
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"redent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
"dev": true,
"requires": {
"indent-string": "^4.0.0",
"strip-indent": "^3.0.0"
}
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
},
"strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
"requires": {
"min-indent": "^1.0.0"
}
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"@testing-library/react": {
@@ -1883,6 +2037,12 @@
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA=="
},
"@types/aria-query": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz",
"integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==",
"dev": true
},
"@types/babel__core": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz",
@@ -1979,6 +2139,16 @@
"@types/istanbul-lib-report": "*"
}
},
"@types/jest": {
"version": "26.0.9",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.9.tgz",
"integrity": "sha512-k4qFfJ5AUKrWok5KYXp2EPm89b0P/KZpl7Vg4XuOTVVQEhLDBDBU3iBFrjjdgd8fLw96aAtmnwhXHl63bWeBQQ==",
"dev": true,
"requires": {
"jest-diff": "^25.2.1",
"pretty-format": "^25.2.1"
}
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -2062,6 +2232,15 @@
"pretty-format": "^25.1.0"
}
},
"@types/testing-library__jest-dom": {
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.2.tgz",
"integrity": "sha512-K7nUSpH/5i8i0NagTJ+uFUDRueDlnMNhJtMjMwTGPPSqyImbWC/hgKPDCKt6Phu2iMJg2kWqlax+Ucj2DKMwpA==",
"dev": true,
"requires": {
"@types/jest": "*"
}
},
"@types/testing-library__react": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-10.0.1.tgz",
@@ -2766,13 +2945,40 @@
}
},
"aria-query": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.0.2.tgz",
"integrity": "sha512-S1G1V790fTaigUSM/Gd0NngzEfiMy9uTUfMyHhKhVyy4cH5O/eTuR01ydhGL0z4Za1PXFTRGH3qL8VhUQuEO5w==",
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
"integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.7.4",
"@babel/runtime-corejs3": "^7.7.4"
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
},
"dependencies": {
"@babel/runtime": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
"integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"@babel/runtime-corejs3": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz",
"integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==",
"dev": true,
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.4"
}
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"dev": true
}
}
},
"arr-diff": {
@@ -4237,25 +4443,29 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"dev": true,
"optional": true,
"requires": {
@@ -4265,13 +4475,15 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"optional": true,
"requires": {
@@ -4281,37 +4493,43 @@
},
"chownr": {
"version": "1.1.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"debug": {
"version": "3.2.6",
"bundled": true,
"resolved": false,
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"optional": true,
"requires": {
@@ -4320,25 +4538,29 @@
},
"deep-extend": {
"version": "0.6.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.7",
"bundled": true,
"resolved": false,
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"dev": true,
"optional": true,
"requires": {
@@ -4347,13 +4569,15 @@
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"optional": true,
"requires": {
@@ -4369,7 +4593,8 @@
},
"glob": {
"version": "7.1.6",
"bundled": true,
"resolved": false,
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"optional": true,
"requires": {
@@ -4383,13 +4608,15 @@
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
"bundled": true,
"resolved": false,
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"optional": true,
"requires": {
@@ -4398,7 +4625,8 @@
},
"ignore-walk": {
"version": "3.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"dev": true,
"optional": true,
"requires": {
@@ -4407,7 +4635,8 @@
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"optional": true,
"requires": {
@@ -4417,19 +4646,22 @@
},
"inherits": {
"version": "2.0.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"optional": true,
"requires": {
@@ -4438,13 +4670,15 @@
},
"isarray": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"optional": true,
"requires": {
@@ -4453,13 +4687,15 @@
},
"minimist": {
"version": "1.2.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true,
"optional": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true,
"optional": true,
"requires": {
@@ -4469,7 +4705,8 @@
},
"minizlib": {
"version": "1.3.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"dev": true,
"optional": true,
"requires": {
@@ -4478,7 +4715,8 @@
},
"mkdirp": {
"version": "0.5.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
"dev": true,
"optional": true,
"requires": {
@@ -4487,13 +4725,15 @@
},
"ms": {
"version": "2.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true,
"optional": true
},
"needle": {
"version": "2.3.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==",
"dev": true,
"optional": true,
"requires": {
@@ -4504,7 +4744,8 @@
},
"node-pre-gyp": {
"version": "0.14.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
"dev": true,
"optional": true,
"requires": {
@@ -4522,7 +4763,8 @@
},
"nopt": {
"version": "4.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
"dev": true,
"optional": true,
"requires": {
@@ -4532,7 +4774,8 @@
},
"npm-bundled": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"dev": true,
"optional": true,
"requires": {
@@ -4541,13 +4784,15 @@
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.4.8",
"bundled": true,
"resolved": false,
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
"dev": true,
"optional": true,
"requires": {
@@ -4558,7 +4803,8 @@
},
"npmlog": {
"version": "4.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"optional": true,
"requires": {
@@ -4570,19 +4816,22 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"optional": true,
"requires": {
@@ -4591,19 +4840,22 @@
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"optional": true,
"requires": {
@@ -4613,19 +4865,22 @@
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.8",
"bundled": true,
"resolved": false,
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"optional": true,
"requires": {
@@ -4637,7 +4892,8 @@
},
"readable-stream": {
"version": "2.3.7",
"bundled": true,
"resolved": false,
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dev": true,
"optional": true,
"requires": {
@@ -4652,7 +4908,8 @@
},
"rimraf": {
"version": "2.7.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"optional": true,
"requires": {
@@ -4661,43 +4918,50 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true,
"optional": true
},
"semver": {
"version": "5.7.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"optional": true,
"requires": {
@@ -4708,7 +4972,8 @@
},
"string_decoder": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
"requires": {
@@ -4717,7 +4982,8 @@
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
"requires": {
@@ -4726,13 +4992,15 @@
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.13",
"bundled": true,
"resolved": false,
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"dev": true,
"optional": true,
"requires": {
@@ -4747,13 +5015,15 @@
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"dev": true,
"optional": true,
"requires": {
@@ -4762,13 +5032,15 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true,
"optional": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"optional": true
}
@@ -5538,6 +5810,29 @@
"randomfill": "^1.0.3"
}
},
"css": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
"integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
"dev": true,
"requires": {
"inherits": "^2.0.4",
"source-map": "^0.6.1",
"source-map-resolve": "^0.6.0"
},
"dependencies": {
"source-map-resolve": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
"integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
"dev": true,
"requires": {
"atob": "^2.1.2",
"decode-uri-component": "^0.2.0"
}
}
}
},
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@@ -5647,6 +5942,12 @@
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
},
"css.escape": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
"integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=",
"dev": true
},
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -6073,9 +6374,9 @@
}
},
"dom-accessibility-api": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.3.tgz",
"integrity": "sha512-JZ8iPuEHDQzq6q0k7PKMGbrIdsgBB7TRrtVOUm4nSMCExlg5qQG4KXWTH2k90yggjM4tTumRGwTKJSldMzKyLA==",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.0.tgz",
"integrity": "sha512-eCVf9n4Ni5UQAFc2+fqfMPHdtiX7DA0rLakXgNBZfXNJzEbNo3MQIYd+zdYpFBqAaGYVrkd8leNSLGPrG4ODmA==",
"dev": true
},
"dom-align": {
@@ -11820,6 +12121,12 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true
},
"mini-create-react-context": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",

View File

@@ -49,10 +49,13 @@
"terser-webpack-plugin": "^2.3.5"
},
"devDependencies": {
"@apollo/react-testing": "3.0.0",
"@babel/core": "^7.8.7",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/preset-env": "^7.8.7",
"@babel/preset-react": "^7.8.3",
"@testing-library/dom": "^7.22.1",
"@testing-library/jest-dom": "^5.11.2",
"@testing-library/react": "^10.0.3",
"babel-core": "^6.26.3",
"babel-eslint": "^10.1.0",