foreman_statistics 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/foreman_statistics/api/v2/statistics_controller.rb +9 -1
- data/app/controllers/foreman_statistics/api/v2/trends_controller.rb +8 -0
- data/db/migrate/20200605153005_migrate_core_types.rb +6 -2
- data/lib/foreman_statistics/engine.rb +1 -1
- data/lib/foreman_statistics/version.rb +1 -1
- data/webpack/__mocks__/foremanReact/API.js +7 -0
- data/webpack/__mocks__/foremanReact/common/HOC.js +24 -0
- data/webpack/__mocks__/foremanReact/common/I18n.js +3 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/common/urlHelpers.js +1 -0
- data/webpack/__mocks__/foremanReact/components/ChartBox/index.js +2 -0
- data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
- data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +10 -0
- data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +4 -0
- data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +2 -0
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
- data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +4 -0
- data/webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +3 -0
- data/webpack/__mocks__/foremanReact/components/common/table.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js +7 -0
- data/webpack/__mocks__/foremanReact/constants.js +24 -0
- data/webpack/__mocks__/foremanReact/readme.md +11 -0
- data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +8 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js +5 -0
- data/webpack/__mocks__/foremanReact/routes/common/reducerHOC/withDataReducer.js +35 -0
- data/webpack/fills_index.js +15 -0
- data/webpack/index.js +21 -0
- data/webpack/src/Components/StatisticsChartsList/StatisticsChartsList.fixtures.js +19 -0
- data/webpack/src/Components/StatisticsChartsList/StatisticsChartsList.test.js +18 -0
- data/webpack/src/Components/StatisticsChartsList/StatisticsChartsListStyles.scss +28 -0
- data/webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap +42 -0
- data/webpack/src/Components/StatisticsChartsList/index.js +32 -0
- data/webpack/src/ForemanStatistics.js +11 -0
- data/webpack/src/Router/StatisticsPage/Statistics/Statistics.js +27 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPage.fixtures.js +11 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPage.js +21 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPageActions.js +43 -0
- data/webpack/src/Router/StatisticsPage/StatisticsPageSelectors.js +12 -0
- data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPage.test.js +12 -0
- data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPageActions.test.js +27 -0
- data/webpack/src/Router/StatisticsPage/__tests__/StatisticsPageSelectors.test.js +32 -0
- data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPage.test.js.snap +32 -0
- data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap +54 -0
- data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap +35 -0
- data/webpack/src/Router/StatisticsPage/constants.js +4 -0
- data/webpack/src/Router/StatisticsPage/index.js +36 -0
- data/webpack/src/Router/__snapshots__/routes.test.js.snap +47 -0
- data/webpack/src/Router/index.js +14 -0
- data/webpack/src/Router/routes.js +11 -0
- data/webpack/src/Router/routes.test.js +27 -0
- data/webpack/src/index.js +1 -0
- data/webpack/src/reducers.js +7 -0
- data/webpack/src/trends.js +7 -0
- data/webpack/src/trends.test.js +44 -0
- metadata +58 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2535d7ffe5c9d9e7d594d61a1ed6e5fbed330c3b5739a3792ce78910c365ef2
|
|
4
|
+
data.tar.gz: 8be508f7afacb9d168013e7d5a101e57dd775107f4d3aa12211b252d5a3bfe94
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1849bc62e6be28e6572727e87afaeba9c9e803643d0de003a0d903a81ba3b5a6a964755a1ebc734054ab1e09cd5116135b34d5392a2eb41e653d956dac1b9973
|
|
7
|
+
data.tar.gz: 0fa5e7507dcc8d5e75755cdbd8fd7e70195e1bc1fa37c037c0d00740da2771446fec24b49dbbed50dfe4b7d6e7d45a7c6386e5261cd5329e49bffac0808f8946
|
|
@@ -2,13 +2,14 @@ module ForemanStatistics
|
|
|
2
2
|
module Api
|
|
3
3
|
module V2
|
|
4
4
|
class StatisticsController < ::Api::V2::BaseController
|
|
5
|
+
before_action :show_deprecation_for_core_routes
|
|
6
|
+
|
|
5
7
|
resource_description do
|
|
6
8
|
api_version 'v2'
|
|
7
9
|
api_base_url '/foreman_statistics/api'
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
api :GET, '/statistics/', N_('Get statistics')
|
|
11
|
-
|
|
12
13
|
def index
|
|
13
14
|
@os_count = Host.authorized(:view_hosts).count_distribution :operatingsystem
|
|
14
15
|
@arch_count = Host.authorized(:view_hosts).count_distribution :architecture
|
|
@@ -27,6 +28,13 @@ module ForemanStatistics
|
|
|
27
28
|
:model_count => @model_count, :mem_size => @mem_size, :mem_free => @mem_free,
|
|
28
29
|
:swap_free => @swap_free, :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree }
|
|
29
30
|
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def show_deprecation_for_core_routes
|
|
35
|
+
return if request.path.starts_with?('/foreman_statistics')
|
|
36
|
+
Foreman::Deprecation.api_deprecation_warning('/api/v2/statistics API endpoint is deprecated, please use /foreman_statistics/api/v2/statistics instead')
|
|
37
|
+
end
|
|
30
38
|
end
|
|
31
39
|
end
|
|
32
40
|
end
|
|
@@ -4,6 +4,7 @@ module ForemanStatistics
|
|
|
4
4
|
class TrendsController < ::Api::V2::BaseController
|
|
5
5
|
include ForemanStatistics::Parameters::Trend
|
|
6
6
|
|
|
7
|
+
before_action :show_deprecation_for_core_routes
|
|
7
8
|
before_action :find_resource, :only => %i[show destroy]
|
|
8
9
|
|
|
9
10
|
TRENDABLE_TYPES = %w[
|
|
@@ -52,6 +53,13 @@ module ForemanStatistics
|
|
|
52
53
|
def resource_scope(options = {})
|
|
53
54
|
@resource_scope ||= scope_for(ForemanStatistics::Trend.types, options)
|
|
54
55
|
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def show_deprecation_for_core_routes
|
|
60
|
+
return if request.path.starts_with?('/foreman_statistics')
|
|
61
|
+
Foreman::Deprecation.api_deprecation_warning('/api/v2/trends API endpoints are deprecated, please use /foreman_statistics/api/v2/trends instead')
|
|
62
|
+
end
|
|
55
63
|
end
|
|
56
64
|
end
|
|
57
65
|
end
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
class MigrateCoreTypes < ActiveRecord::Migration[6.0]
|
|
2
|
+
class FakeTrend < ApplicationRecord
|
|
3
|
+
self.table_name = 'trends'
|
|
4
|
+
end
|
|
5
|
+
|
|
2
6
|
def up
|
|
3
7
|
Permission.where(:resource_type => 'Trend').update_all(:resource_type => 'ForemanStatistics::Trend')
|
|
4
8
|
%w[ForemanTrend FactTrend Trend].each do |t|
|
|
5
|
-
|
|
9
|
+
FakeTrend.where(:type => t).update_all(:type => "ForemanStatistics::#{t}")
|
|
6
10
|
end
|
|
7
11
|
end
|
|
8
12
|
|
|
9
13
|
def down
|
|
10
14
|
Permission.where(:resource_type => 'ForemanStatistics::Trend').update_all(:resource_type => 'Trend')
|
|
11
15
|
%w[ForemanTrend FactTrend Trend].each do |t|
|
|
12
|
-
|
|
16
|
+
FakeTrend.where(:type => "ForemanStatistics::#{t}").update_all(:type => t)
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
19
|
end
|
|
@@ -8,7 +8,7 @@ module ForemanStatistics
|
|
|
8
8
|
config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
|
|
9
9
|
config.autoload_paths += Dir["#{config.root}/app/overrides"]
|
|
10
10
|
|
|
11
|
-
config.paths['db/migrate'] << 'db/migrate_foreman'
|
|
11
|
+
config.paths['db/migrate'] << 'db/migrate_foreman' unless Gem::Version.new(SETTINGS[:version]).release < Gem::Version.new('2.2')
|
|
12
12
|
|
|
13
13
|
# Add any db migrations
|
|
14
14
|
initializer 'foreman_statistics.load_app_instance_data' do |app|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export const callOnMount = callback => WrappedComponent => componentProps => {
|
|
4
|
+
// fires callback onMount, [] means don't listen to any props change
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
callback(componentProps);
|
|
7
|
+
}, [componentProps]);
|
|
8
|
+
|
|
9
|
+
return <WrappedComponent {...componentProps} />;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const withRenderHandler = ({
|
|
13
|
+
Component,
|
|
14
|
+
LoadingComponent = () => jest.fn(),
|
|
15
|
+
ErrorComponent = () => jest.fn(),
|
|
16
|
+
EmptyComponent = () => jest.fn(),
|
|
17
|
+
}) => componentProps => {
|
|
18
|
+
const { isLoading, hasData, hasError } = componentProps;
|
|
19
|
+
|
|
20
|
+
if (isLoading && !hasData) return <LoadingComponent {...componentProps} />;
|
|
21
|
+
if (hasError) return <ErrorComponent {...componentProps} />;
|
|
22
|
+
if (hasData) return <Component {...componentProps} />;
|
|
23
|
+
return <EmptyComponent {...componentProps} />;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const noop = Function.prototype;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const getURIsearch = () => 'a=b';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const STATUS = {
|
|
2
|
+
PENDING: 'PENDING',
|
|
3
|
+
RESOLVED: 'RESOLVED',
|
|
4
|
+
ERROR: 'ERROR',
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const getControllerSearchProps = (
|
|
8
|
+
controller,
|
|
9
|
+
id = 'searchBar',
|
|
10
|
+
canCreateBookmarks = true
|
|
11
|
+
) => ({
|
|
12
|
+
controller,
|
|
13
|
+
autocomplete: {
|
|
14
|
+
id,
|
|
15
|
+
searchQuery: '',
|
|
16
|
+
url: `${controller}/auto_complete_search`,
|
|
17
|
+
useKeyShortcuts: true,
|
|
18
|
+
},
|
|
19
|
+
bookmarks: {
|
|
20
|
+
url: '/api/bookmarks',
|
|
21
|
+
canCreateBookmarks,
|
|
22
|
+
documentationUrl: `4.1.5Searching`,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
For testing components which have imported foreman-core components,
|
|
2
|
+
a mock file is required in this folder.
|
|
3
|
+
|
|
4
|
+
### Example: Mocking ForemanModal component
|
|
5
|
+
```js
|
|
6
|
+
// __mocks__/foremanReact/components/ForemanModal/index.js
|
|
7
|
+
const ForemanModal = () => jest.fn();
|
|
8
|
+
ForemanModal.Header = () => jest.fn();
|
|
9
|
+
ForemanModal.Footer = () => jest.fn();
|
|
10
|
+
export default ForemanModal;
|
|
11
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Immutable from 'seamless-immutable';
|
|
2
|
+
|
|
3
|
+
const initialState = Immutable({
|
|
4
|
+
isLoading: true,
|
|
5
|
+
hasError: false,
|
|
6
|
+
hasData: false,
|
|
7
|
+
message: { type: 'empty', text: '' },
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const withDataReducer = controller => (
|
|
11
|
+
state = initialState,
|
|
12
|
+
{ type, payload }
|
|
13
|
+
) => {
|
|
14
|
+
switch (type) {
|
|
15
|
+
case `${controller}_DATA_RESOLVED`:
|
|
16
|
+
return state.merge({ ...payload, isLoading: false });
|
|
17
|
+
|
|
18
|
+
case `${controller}_DATA_FAILED`:
|
|
19
|
+
return state.merge({ ...payload, isLoading: false, hasError: true });
|
|
20
|
+
|
|
21
|
+
case `${controller}_CLEAR_ERROR`:
|
|
22
|
+
return state.set('hasError', false);
|
|
23
|
+
|
|
24
|
+
case `${controller}_SHOW_LOADING`:
|
|
25
|
+
return state.set('isLoading', true);
|
|
26
|
+
|
|
27
|
+
case `${controller}_HIDE_LOADING`:
|
|
28
|
+
return state.set('isLoading', false);
|
|
29
|
+
|
|
30
|
+
default:
|
|
31
|
+
return state;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default withDataReducer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This example for extanding foreman-core's component via slot&fill
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
|
|
6
|
+
|
|
7
|
+
addGlobalFill('slotId', 'fillId', <SomeComponent key="some-key" />, 300);
|
|
8
|
+
|
|
9
|
+
addGlobalFill(
|
|
10
|
+
'slotId',
|
|
11
|
+
'fillId',
|
|
12
|
+
{ someProp: 'this is an override prop' },
|
|
13
|
+
300
|
|
14
|
+
);
|
|
15
|
+
*/
|
data/webpack/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint import/no-unresolved: [2, { ignore: [foremanReact/*] }] */
|
|
2
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
3
|
+
/* eslint-disable import/extensions */
|
|
4
|
+
import componentRegistry from 'foremanReact/components/componentRegistry';
|
|
5
|
+
import { registerReducer } from 'foremanReact/common/MountingService';
|
|
6
|
+
import reducers from './src/reducers';
|
|
7
|
+
import ForemanStatistics from './src/ForemanStatistics';
|
|
8
|
+
import * as trends from './src/trends';
|
|
9
|
+
|
|
10
|
+
Object.assign(window.tfm, { trends });
|
|
11
|
+
|
|
12
|
+
// register reducers
|
|
13
|
+
Object.entries(reducers).forEach(([key, reducer]) =>
|
|
14
|
+
registerReducer(key, reducer)
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// register components for erb mounting
|
|
18
|
+
componentRegistry.register({
|
|
19
|
+
name: 'ForemanStatistics',
|
|
20
|
+
type: ForemanStatistics,
|
|
21
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const statisticsData = {
|
|
2
|
+
operatingsystem: {
|
|
3
|
+
id: 'operatingsystem',
|
|
4
|
+
title: 'OS Distribution',
|
|
5
|
+
url: 'statistics/operatingsystem',
|
|
6
|
+
search: '/hosts?search=os_title=~VAL~',
|
|
7
|
+
},
|
|
8
|
+
architecture: {
|
|
9
|
+
id: 'architecture',
|
|
10
|
+
title: 'Architecture Distribution',
|
|
11
|
+
url: 'statistics/architecture',
|
|
12
|
+
search: '/hosts?search=facts.architecture=~VAL~',
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const statisticsMeta = [
|
|
17
|
+
statisticsData.operatingsystem,
|
|
18
|
+
statisticsData.architecture,
|
|
19
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
|
|
2
|
+
|
|
3
|
+
import StatisticsChartsList from './';
|
|
4
|
+
import { statisticsData } from './StatisticsChartsList.fixtures';
|
|
5
|
+
|
|
6
|
+
const fixtures = {
|
|
7
|
+
'should render no panels for empty data': {
|
|
8
|
+
data: {},
|
|
9
|
+
},
|
|
10
|
+
'should render two panels for fixtures data': {
|
|
11
|
+
data: statisticsData,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('StatisticsChartsList', () => {
|
|
16
|
+
describe('rendering', () =>
|
|
17
|
+
testComponentSnapshotsWithFixtures(StatisticsChartsList, fixtures));
|
|
18
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.statistics-charts-list-root {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
justify-content: space-around;
|
|
5
|
+
padding-top: 20px;
|
|
6
|
+
.chart-box {
|
|
7
|
+
height: 312px;
|
|
8
|
+
width: 280px;
|
|
9
|
+
display: -ms-flexbox;
|
|
10
|
+
display: flex;
|
|
11
|
+
-ms-flex-direction: column;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
min-height: 312px;
|
|
14
|
+
|
|
15
|
+
.card-pf-heading {
|
|
16
|
+
-ms-flex: 0 0 30px;
|
|
17
|
+
flex: 0 0 30px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.card-pf-body {
|
|
21
|
+
display: -ms-flexbox;
|
|
22
|
+
display: flex;
|
|
23
|
+
-ms-flex: 1;
|
|
24
|
+
flex: 1;
|
|
25
|
+
margin: 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
data/webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`StatisticsChartsList rendering should render no panels for empty data 1`] = `
|
|
4
|
+
<div
|
|
5
|
+
className="statistics-charts-list-root cards-pf"
|
|
6
|
+
/>
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
exports[`StatisticsChartsList rendering should render two panels for fixtures data 1`] = `
|
|
10
|
+
<div
|
|
11
|
+
className="statistics-charts-list-root cards-pf"
|
|
12
|
+
>
|
|
13
|
+
<ConnectedChartBox
|
|
14
|
+
chart={
|
|
15
|
+
Object {
|
|
16
|
+
"id": "operatingsystem",
|
|
17
|
+
"search": "/hosts?search=os_title=~VAL~",
|
|
18
|
+
"title": "OS Distribution",
|
|
19
|
+
"url": "statistics/operatingsystem",
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
key="operatingsystem"
|
|
23
|
+
noDataMsg="No data available"
|
|
24
|
+
tip="Expand the chart"
|
|
25
|
+
type="donut"
|
|
26
|
+
/>
|
|
27
|
+
<ConnectedChartBox
|
|
28
|
+
chart={
|
|
29
|
+
Object {
|
|
30
|
+
"id": "architecture",
|
|
31
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
|
32
|
+
"title": "Architecture Distribution",
|
|
33
|
+
"url": "statistics/architecture",
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
key="architecture"
|
|
37
|
+
noDataMsg="No data available"
|
|
38
|
+
tip="Expand the chart"
|
|
39
|
+
type="donut"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
5
|
+
import ConnectedChartBox from 'foremanReact/components/ChartBox';
|
|
6
|
+
import './StatisticsChartsListStyles.scss';
|
|
7
|
+
|
|
8
|
+
const StatisticsChartsList = ({ data }) => {
|
|
9
|
+
const chartBoxes = Object.values(data).map(chart => (
|
|
10
|
+
<ConnectedChartBox
|
|
11
|
+
key={chart.id}
|
|
12
|
+
type="donut"
|
|
13
|
+
chart={chart}
|
|
14
|
+
noDataMsg={__('No data available')}
|
|
15
|
+
tip={__('Expand the chart')}
|
|
16
|
+
/>
|
|
17
|
+
));
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="statistics-charts-list-root cards-pf">{chartBoxes}</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
StatisticsChartsList.propTypes = {
|
|
25
|
+
data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
StatisticsChartsList.defaultProps = {
|
|
29
|
+
data: [],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default StatisticsChartsList;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
3
|
+
import ForemanStatisticsRoute from './Router';
|
|
4
|
+
|
|
5
|
+
const ForemanStatistics = () => (
|
|
6
|
+
<BrowserRouter>
|
|
7
|
+
<ForemanStatisticsRoute />
|
|
8
|
+
</BrowserRouter>
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export default ForemanStatistics;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { withRenderHandler } from 'foremanReact/common/HOC';
|
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
5
|
+
import { EmptyStatePattern } from 'foremanReact/components/common/EmptyState';
|
|
6
|
+
import StatisticsChartsList from '../../../Components/StatisticsChartsList';
|
|
7
|
+
|
|
8
|
+
const Statistics = ({ statisticsMeta }) => {
|
|
9
|
+
if (!statisticsMeta) {
|
|
10
|
+
return (
|
|
11
|
+
<EmptyStatePattern
|
|
12
|
+
icon="info"
|
|
13
|
+
header={__('No Charts To Load')}
|
|
14
|
+
description=""
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return <StatisticsChartsList data={statisticsMeta} />;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
Statistics.propTypes = {
|
|
22
|
+
statisticsMeta: PropTypes.array.isRequired,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default withRenderHandler({
|
|
26
|
+
Component: Statistics,
|
|
27
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { noop } from 'foremanReact/common/helpers';
|
|
2
|
+
import { statisticsMeta } from '../../Components/StatisticsChartsList/StatisticsChartsList.fixtures';
|
|
3
|
+
|
|
4
|
+
export const statisticsProps = {
|
|
5
|
+
statisticsMeta,
|
|
6
|
+
isLoading: false,
|
|
7
|
+
hasData: true,
|
|
8
|
+
hasError: false,
|
|
9
|
+
message: {},
|
|
10
|
+
getStatisticsMeta: noop,
|
|
11
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
|
+
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
|
5
|
+
import Statistics from './Statistics/Statistics';
|
|
6
|
+
|
|
7
|
+
const StatisticsPage = ({ statisticsMeta, ...props }) => (
|
|
8
|
+
<PageLayout header={__('Statistics')} searchable={false}>
|
|
9
|
+
<Statistics statisticsMeta={statisticsMeta} {...props} />
|
|
10
|
+
</PageLayout>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
StatisticsPage.propTypes = {
|
|
14
|
+
statisticsMeta: PropTypes.array,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
StatisticsPage.defaultProps = {
|
|
18
|
+
statisticsMeta: [],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default StatisticsPage;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import API from 'foremanReact/API';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
STATISTICS_PAGE_DATA_RESOLVED,
|
|
5
|
+
STATISTICS_PAGE_DATA_FAILED,
|
|
6
|
+
STATISTICS_PAGE_HIDE_LOADING,
|
|
7
|
+
STATISTICS_PAGE_URL,
|
|
8
|
+
} from './constants';
|
|
9
|
+
|
|
10
|
+
export const getStatisticsMeta = (
|
|
11
|
+
url = STATISTICS_PAGE_URL
|
|
12
|
+
) => async dispatch => {
|
|
13
|
+
const onFetchSuccess = ({ data }) => {
|
|
14
|
+
dispatch(hideLoading());
|
|
15
|
+
dispatch({
|
|
16
|
+
type: STATISTICS_PAGE_DATA_RESOLVED,
|
|
17
|
+
payload: { metadata: data.charts, hasData: data.charts.length > 0 },
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const onFetchError = ({ message }) => {
|
|
22
|
+
dispatch(hideLoading());
|
|
23
|
+
dispatch({
|
|
24
|
+
type: STATISTICS_PAGE_DATA_FAILED,
|
|
25
|
+
payload: {
|
|
26
|
+
message: {
|
|
27
|
+
type: 'error',
|
|
28
|
+
text: message,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
const response = await API.get(url);
|
|
35
|
+
return onFetchSuccess(response);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return onFetchError(error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const hideLoading = () => ({
|
|
42
|
+
type: STATISTICS_PAGE_HIDE_LOADING,
|
|
43
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const selectStatisticsPage = state => state.statisticsPage;
|
|
2
|
+
|
|
3
|
+
export const selectStatisticsMetadata = state =>
|
|
4
|
+
selectStatisticsPage(state).metadata;
|
|
5
|
+
export const selectStatisticsIsLoading = state =>
|
|
6
|
+
selectStatisticsPage(state).isLoading;
|
|
7
|
+
export const selectStatisticsMessage = state =>
|
|
8
|
+
selectStatisticsPage(state).message;
|
|
9
|
+
export const selectStatisticsHasError = state =>
|
|
10
|
+
selectStatisticsPage(state).hasError;
|
|
11
|
+
export const selectStatisticsHasMetadata = state =>
|
|
12
|
+
selectStatisticsPage(state).hasData;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
|
|
2
|
+
import { statisticsProps } from '../StatisticsPage.fixtures';
|
|
3
|
+
import StatisticsPage from '../StatisticsPage';
|
|
4
|
+
|
|
5
|
+
const fixtures = {
|
|
6
|
+
'render with props': statisticsProps,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe('StatisticsPage', () => {
|
|
10
|
+
describe('rendering', () =>
|
|
11
|
+
testComponentSnapshotsWithFixtures(StatisticsPage, fixtures));
|
|
12
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import API from 'foremanReact/API';
|
|
2
|
+
|
|
3
|
+
import { testActionSnapshotWithFixtures } from '@theforeman/test';
|
|
4
|
+
import { getStatisticsMeta } from '../StatisticsPageActions';
|
|
5
|
+
import { statisticsProps } from '../StatisticsPage.fixtures';
|
|
6
|
+
|
|
7
|
+
jest.mock('foremanReact/API');
|
|
8
|
+
|
|
9
|
+
const runStatisticsAction = (callback, props, serverMock) => {
|
|
10
|
+
API.get.mockImplementation(serverMock);
|
|
11
|
+
|
|
12
|
+
return callback(props);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const fixtures = {
|
|
16
|
+
'should fetch statisticsMeta': () =>
|
|
17
|
+
runStatisticsAction(getStatisticsMeta, {}, async () => ({
|
|
18
|
+
data: { charts: statisticsProps.statisticsMeta },
|
|
19
|
+
})),
|
|
20
|
+
'should fetch statisticsMeta and fail': () =>
|
|
21
|
+
runStatisticsAction(getStatisticsMeta, {}, async () => {
|
|
22
|
+
throw new Error('some-error');
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('StatisticsPage actions', () =>
|
|
27
|
+
testActionSnapshotWithFixtures(fixtures));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
|
|
2
|
+
import {
|
|
3
|
+
selectStatisticsPage,
|
|
4
|
+
selectStatisticsMetadata,
|
|
5
|
+
selectStatisticsHasMetadata,
|
|
6
|
+
selectStatisticsIsLoading,
|
|
7
|
+
selectStatisticsMessage,
|
|
8
|
+
selectStatisticsHasError,
|
|
9
|
+
} from '../StatisticsPageSelectors';
|
|
10
|
+
import { statisticsProps } from '../StatisticsPage.fixtures';
|
|
11
|
+
|
|
12
|
+
const state = {
|
|
13
|
+
statisticsPage: {
|
|
14
|
+
...statisticsProps,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const fixtures = {
|
|
19
|
+
'should return StatisticsPage': () => selectStatisticsPage(state),
|
|
20
|
+
'should return StatisticsHasMetadata': () =>
|
|
21
|
+
selectStatisticsHasMetadata(state),
|
|
22
|
+
'should return StatisticsPage statisticsMeta': () =>
|
|
23
|
+
selectStatisticsMetadata(state),
|
|
24
|
+
'should return StatisticsPage isLoading': () =>
|
|
25
|
+
selectStatisticsIsLoading(state),
|
|
26
|
+
'should return StatisticsPage Message': () => selectStatisticsMessage(state),
|
|
27
|
+
'should return StatisticsPage hasError': () =>
|
|
28
|
+
selectStatisticsHasError(state),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe('StatisticsPage selectors', () =>
|
|
32
|
+
testSelectorsSnapshotWithFixtures(fixtures));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`StatisticsPage rendering render with props 1`] = `
|
|
4
|
+
<PageLayout
|
|
5
|
+
header="Statistics"
|
|
6
|
+
searchable={false}
|
|
7
|
+
>
|
|
8
|
+
<Component
|
|
9
|
+
getStatisticsMeta={[Function]}
|
|
10
|
+
hasData={true}
|
|
11
|
+
hasError={false}
|
|
12
|
+
isLoading={false}
|
|
13
|
+
message={Object {}}
|
|
14
|
+
statisticsMeta={
|
|
15
|
+
Array [
|
|
16
|
+
Object {
|
|
17
|
+
"id": "operatingsystem",
|
|
18
|
+
"search": "/hosts?search=os_title=~VAL~",
|
|
19
|
+
"title": "OS Distribution",
|
|
20
|
+
"url": "statistics/operatingsystem",
|
|
21
|
+
},
|
|
22
|
+
Object {
|
|
23
|
+
"id": "architecture",
|
|
24
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
|
25
|
+
"title": "Architecture Distribution",
|
|
26
|
+
"url": "statistics/architecture",
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
/>
|
|
31
|
+
</PageLayout>
|
|
32
|
+
`;
|
data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`StatisticsPage actions should fetch statisticsMeta 1`] = `
|
|
4
|
+
Array [
|
|
5
|
+
Array [
|
|
6
|
+
Object {
|
|
7
|
+
"type": "STATISTICS_PAGE_HIDE_LOADING",
|
|
8
|
+
},
|
|
9
|
+
],
|
|
10
|
+
Array [
|
|
11
|
+
Object {
|
|
12
|
+
"payload": Object {
|
|
13
|
+
"hasData": true,
|
|
14
|
+
"metadata": Array [
|
|
15
|
+
Object {
|
|
16
|
+
"id": "operatingsystem",
|
|
17
|
+
"search": "/hosts?search=os_title=~VAL~",
|
|
18
|
+
"title": "OS Distribution",
|
|
19
|
+
"url": "statistics/operatingsystem",
|
|
20
|
+
},
|
|
21
|
+
Object {
|
|
22
|
+
"id": "architecture",
|
|
23
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
|
24
|
+
"title": "Architecture Distribution",
|
|
25
|
+
"url": "statistics/architecture",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
"type": "STATISTICS_PAGE_DATA_RESOLVED",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
]
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
exports[`StatisticsPage actions should fetch statisticsMeta and fail 1`] = `
|
|
36
|
+
Array [
|
|
37
|
+
Array [
|
|
38
|
+
Object {
|
|
39
|
+
"type": "STATISTICS_PAGE_HIDE_LOADING",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
Array [
|
|
43
|
+
Object {
|
|
44
|
+
"payload": Object {
|
|
45
|
+
"message": Object {
|
|
46
|
+
"text": "some-error",
|
|
47
|
+
"type": "error",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
"type": "STATISTICS_PAGE_FETCH_FAILED",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
]
|
|
54
|
+
`;
|
data/webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`StatisticsPage selectors should return StatisticsHasMetadata 1`] = `true`;
|
|
4
|
+
|
|
5
|
+
exports[`StatisticsPage selectors should return StatisticsPage 1`] = `
|
|
6
|
+
Object {
|
|
7
|
+
"getStatisticsMeta": [Function],
|
|
8
|
+
"hasData": true,
|
|
9
|
+
"hasError": false,
|
|
10
|
+
"isLoading": false,
|
|
11
|
+
"message": Object {},
|
|
12
|
+
"statisticsMeta": Array [
|
|
13
|
+
Object {
|
|
14
|
+
"id": "operatingsystem",
|
|
15
|
+
"search": "/hosts?search=os_title=~VAL~",
|
|
16
|
+
"title": "OS Distribution",
|
|
17
|
+
"url": "statistics/operatingsystem",
|
|
18
|
+
},
|
|
19
|
+
Object {
|
|
20
|
+
"id": "architecture",
|
|
21
|
+
"search": "/hosts?search=facts.architecture=~VAL~",
|
|
22
|
+
"title": "Architecture Distribution",
|
|
23
|
+
"url": "statistics/architecture",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
exports[`StatisticsPage selectors should return StatisticsPage Message 1`] = `Object {}`;
|
|
30
|
+
|
|
31
|
+
exports[`StatisticsPage selectors should return StatisticsPage hasError 1`] = `false`;
|
|
32
|
+
|
|
33
|
+
exports[`StatisticsPage selectors should return StatisticsPage isLoading 1`] = `false`;
|
|
34
|
+
|
|
35
|
+
exports[`StatisticsPage selectors should return StatisticsPage statisticsMeta 1`] = `undefined`;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const STATISTICS_PAGE_DATA_RESOLVED = 'STATISTICS_PAGE_DATA_RESOLVED';
|
|
2
|
+
export const STATISTICS_PAGE_DATA_FAILED = 'STATISTICS_PAGE_FETCH_FAILED';
|
|
3
|
+
export const STATISTICS_PAGE_HIDE_LOADING = 'STATISTICS_PAGE_HIDE_LOADING';
|
|
4
|
+
export const STATISTICS_PAGE_URL = '/foreman_statistics/statistics';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { compose, bindActionCreators } from 'redux';
|
|
2
|
+
import { connect } from 'react-redux';
|
|
3
|
+
import { callOnMount } from 'foremanReact/common/HOC';
|
|
4
|
+
import withDataReducer from 'foremanReact/routes/common/reducerHOC/withDataReducer';
|
|
5
|
+
|
|
6
|
+
import * as actions from './StatisticsPageActions';
|
|
7
|
+
import {
|
|
8
|
+
selectStatisticsMetadata,
|
|
9
|
+
selectStatisticsMessage,
|
|
10
|
+
selectStatisticsIsLoading,
|
|
11
|
+
selectStatisticsHasMetadata,
|
|
12
|
+
selectStatisticsHasError,
|
|
13
|
+
} from './StatisticsPageSelectors';
|
|
14
|
+
|
|
15
|
+
import StatisticsPage from './StatisticsPage';
|
|
16
|
+
|
|
17
|
+
// map state to props
|
|
18
|
+
const mapStateToProps = state => ({
|
|
19
|
+
statisticsMeta: selectStatisticsMetadata(state),
|
|
20
|
+
isLoading: selectStatisticsIsLoading(state),
|
|
21
|
+
message: selectStatisticsMessage(state),
|
|
22
|
+
hasData: selectStatisticsHasMetadata(state),
|
|
23
|
+
hasError: selectStatisticsHasError(state),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// map action dispatchers to props
|
|
27
|
+
const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
|
|
28
|
+
|
|
29
|
+
// export reducers
|
|
30
|
+
export const reducers = { statisticsPage: withDataReducer('STATISTICS_PAGE') };
|
|
31
|
+
|
|
32
|
+
// export connected component
|
|
33
|
+
export default compose(
|
|
34
|
+
connect(mapStateToProps, mapDispatchToProps),
|
|
35
|
+
callOnMount(({ getStatisticsMeta }) => getStatisticsMeta())
|
|
36
|
+
)(StatisticsPage);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`ForemanPluginTemplateRoutes should create routes 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"statistics": Object {
|
|
6
|
+
"component": Object {
|
|
7
|
+
"$$typeof": Symbol(react.memo),
|
|
8
|
+
"WrappedComponent": [Function],
|
|
9
|
+
"compare": null,
|
|
10
|
+
"displayName": "Connect(Component)",
|
|
11
|
+
"type": [Function],
|
|
12
|
+
},
|
|
13
|
+
"exact": true,
|
|
14
|
+
"path": "/foreman_statistics/statistics",
|
|
15
|
+
"renderResult": <ContextProvider
|
|
16
|
+
value={
|
|
17
|
+
Object {
|
|
18
|
+
"store": Object {
|
|
19
|
+
"dispatch": [MockFunction],
|
|
20
|
+
"getState": [Function],
|
|
21
|
+
"subscribe": [MockFunction],
|
|
22
|
+
},
|
|
23
|
+
"subscription": Subscription {
|
|
24
|
+
"handleChangeWrapper": [Function],
|
|
25
|
+
"listeners": Object {
|
|
26
|
+
"notify": [Function],
|
|
27
|
+
},
|
|
28
|
+
"onStateChange": [Function],
|
|
29
|
+
"parentSub": undefined,
|
|
30
|
+
"store": Object {
|
|
31
|
+
"dispatch": [MockFunction],
|
|
32
|
+
"getState": [Function],
|
|
33
|
+
"subscribe": [MockFunction],
|
|
34
|
+
},
|
|
35
|
+
"unsubscribe": null,
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
<Connect(Component)
|
|
41
|
+
history={Object {}}
|
|
42
|
+
some="props"
|
|
43
|
+
/>
|
|
44
|
+
</ContextProvider>,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Switch, Route } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import routes from './routes';
|
|
5
|
+
|
|
6
|
+
const Router = () => (
|
|
7
|
+
<Switch>
|
|
8
|
+
{Object.entries(routes).map(([key, props]) => (
|
|
9
|
+
<Route key={key} {...props} />
|
|
10
|
+
))}
|
|
11
|
+
</Switch>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export default Router;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Provider } from 'react-redux';
|
|
3
|
+
import { shallow } from '@theforeman/test';
|
|
4
|
+
|
|
5
|
+
import { statisticsProps } from './StatisticsPage/StatisticsPage.fixtures';
|
|
6
|
+
import Routes from './routes';
|
|
7
|
+
|
|
8
|
+
describe('ForemanPluginTemplateRoutes', () => {
|
|
9
|
+
it('should create routes', () => {
|
|
10
|
+
Object.entries(Routes).forEach(([key, Route]) => {
|
|
11
|
+
const store = {
|
|
12
|
+
subscribe: jest.fn(),
|
|
13
|
+
dispatch: jest.fn(),
|
|
14
|
+
getState: () => statisticsProps,
|
|
15
|
+
};
|
|
16
|
+
const RouteComponent = Route.component;
|
|
17
|
+
const component = shallow(
|
|
18
|
+
<Provider store={store}>
|
|
19
|
+
<RouteComponent history={{}} some="props" />
|
|
20
|
+
</Provider>
|
|
21
|
+
);
|
|
22
|
+
Route.renderResult = component;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(Routes).toMatchSnapshot();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './ForemanStatistics';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* eslint-disable jquery/no-val */
|
|
2
|
+
/* eslint-disable jquery/no-is */
|
|
3
|
+
|
|
4
|
+
import $ from 'jquery';
|
|
5
|
+
|
|
6
|
+
jest.unmock('jquery');
|
|
7
|
+
jest.unmock('./trends');
|
|
8
|
+
window.trends = require('./trends');
|
|
9
|
+
|
|
10
|
+
describe('selecting trend type', () => {
|
|
11
|
+
it('should disable fields on non-fact trend', () => {
|
|
12
|
+
document.body.innerHTML = `<select id="trendable_type" onchange="trends.trendTypeSelected(this)">
|
|
13
|
+
<option value="FactName">Facts</option>
|
|
14
|
+
<option value="Hostgroup">Host group</option>
|
|
15
|
+
</select>
|
|
16
|
+
<select id="trend_trendable_id">
|
|
17
|
+
<option value=""></option>
|
|
18
|
+
<option value="27">architecture</option>
|
|
19
|
+
</select>
|
|
20
|
+
<input id="trend_name">`;
|
|
21
|
+
$('#trendable_type')
|
|
22
|
+
.val('Hostgroup')
|
|
23
|
+
.change();
|
|
24
|
+
expect($('#trend_trendable_id').is(':disabled')).toBeTruthy();
|
|
25
|
+
expect($('#trend_name').is(':disabled')).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should enable fields on non-fact trend', () => {
|
|
29
|
+
document.body.innerHTML = `<select id="trendable_type" onchange="trends.trendTypeSelected(this)">
|
|
30
|
+
<option value="Evironment">Environment</option>
|
|
31
|
+
<option value="FactName">Facts</option>
|
|
32
|
+
</select>
|
|
33
|
+
<select id="trend_trendable_id" disabled>
|
|
34
|
+
<option value=""></option>
|
|
35
|
+
<option value="27">architecture</option>
|
|
36
|
+
</select>
|
|
37
|
+
<input id="trend_name" disabled>`;
|
|
38
|
+
$('#trendable_type')
|
|
39
|
+
.val('FactName')
|
|
40
|
+
.change();
|
|
41
|
+
expect($('#trend_trendable_id').is(':disabled')).toBeFalsy();
|
|
42
|
+
expect($('#trend_name').is(':disabled')).toBeFalsy();
|
|
43
|
+
});
|
|
44
|
+
});
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_statistics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ondrej Ezr
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-07-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rdoc
|
|
@@ -28,16 +28,16 @@ dependencies:
|
|
|
28
28
|
name: rubocop
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
33
|
+
version: '0.83'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
40
|
+
version: '0.83'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rubocop-minitest
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -159,6 +159,58 @@ files:
|
|
|
159
159
|
- test/unit/foreman_statistics/statistics_test.rb
|
|
160
160
|
- test/unit/foreman_statistics_test.rb
|
|
161
161
|
- test/unit/tasks/foreman_statistics_tasks_test.rb
|
|
162
|
+
- webpack/__mocks__/foremanReact/API.js
|
|
163
|
+
- webpack/__mocks__/foremanReact/common/HOC.js
|
|
164
|
+
- webpack/__mocks__/foremanReact/common/I18n.js
|
|
165
|
+
- webpack/__mocks__/foremanReact/common/helpers.js
|
|
166
|
+
- webpack/__mocks__/foremanReact/common/urlHelpers.js
|
|
167
|
+
- webpack/__mocks__/foremanReact/components/ChartBox/index.js
|
|
168
|
+
- webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js
|
|
169
|
+
- webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js
|
|
170
|
+
- webpack/__mocks__/foremanReact/components/ForemanModal/index.js
|
|
171
|
+
- webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js
|
|
172
|
+
- webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js
|
|
173
|
+
- webpack/__mocks__/foremanReact/components/common/EmptyState.js
|
|
174
|
+
- webpack/__mocks__/foremanReact/components/common/MessageBox.js
|
|
175
|
+
- webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js
|
|
176
|
+
- webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js
|
|
177
|
+
- webpack/__mocks__/foremanReact/components/common/table.js
|
|
178
|
+
- webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js
|
|
179
|
+
- webpack/__mocks__/foremanReact/constants.js
|
|
180
|
+
- webpack/__mocks__/foremanReact/readme.md
|
|
181
|
+
- webpack/__mocks__/foremanReact/redux/actions/toasts.js
|
|
182
|
+
- webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js
|
|
183
|
+
- webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js
|
|
184
|
+
- webpack/__mocks__/foremanReact/routes/common/reducerHOC/withDataReducer.js
|
|
185
|
+
- webpack/fills_index.js
|
|
186
|
+
- webpack/index.js
|
|
187
|
+
- webpack/src/Components/StatisticsChartsList/StatisticsChartsList.fixtures.js
|
|
188
|
+
- webpack/src/Components/StatisticsChartsList/StatisticsChartsList.test.js
|
|
189
|
+
- webpack/src/Components/StatisticsChartsList/StatisticsChartsListStyles.scss
|
|
190
|
+
- webpack/src/Components/StatisticsChartsList/__snapshots__/StatisticsChartsList.test.js.snap
|
|
191
|
+
- webpack/src/Components/StatisticsChartsList/index.js
|
|
192
|
+
- webpack/src/ForemanStatistics.js
|
|
193
|
+
- webpack/src/Router/StatisticsPage/Statistics/Statistics.js
|
|
194
|
+
- webpack/src/Router/StatisticsPage/StatisticsPage.fixtures.js
|
|
195
|
+
- webpack/src/Router/StatisticsPage/StatisticsPage.js
|
|
196
|
+
- webpack/src/Router/StatisticsPage/StatisticsPageActions.js
|
|
197
|
+
- webpack/src/Router/StatisticsPage/StatisticsPageSelectors.js
|
|
198
|
+
- webpack/src/Router/StatisticsPage/__tests__/StatisticsPage.test.js
|
|
199
|
+
- webpack/src/Router/StatisticsPage/__tests__/StatisticsPageActions.test.js
|
|
200
|
+
- webpack/src/Router/StatisticsPage/__tests__/StatisticsPageSelectors.test.js
|
|
201
|
+
- webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPage.test.js.snap
|
|
202
|
+
- webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageActions.test.js.snap
|
|
203
|
+
- webpack/src/Router/StatisticsPage/__tests__/__snapshots__/StatisticsPageSelectors.test.js.snap
|
|
204
|
+
- webpack/src/Router/StatisticsPage/constants.js
|
|
205
|
+
- webpack/src/Router/StatisticsPage/index.js
|
|
206
|
+
- webpack/src/Router/__snapshots__/routes.test.js.snap
|
|
207
|
+
- webpack/src/Router/index.js
|
|
208
|
+
- webpack/src/Router/routes.js
|
|
209
|
+
- webpack/src/Router/routes.test.js
|
|
210
|
+
- webpack/src/index.js
|
|
211
|
+
- webpack/src/reducers.js
|
|
212
|
+
- webpack/src/trends.js
|
|
213
|
+
- webpack/src/trends.test.js
|
|
162
214
|
homepage: https://theforeman.org
|
|
163
215
|
licenses:
|
|
164
216
|
- GPL-3.0
|