diff --git a/src/gmp/capabilities/capabilities.js b/src/gmp/capabilities/capabilities.js index 0eb3210864..64a0c69eaa 100644 --- a/src/gmp/capabilities/capabilities.js +++ b/src/gmp/capabilities/capabilities.js @@ -44,6 +44,8 @@ const types = { policies: 'config', portlist: 'port_list', portlists: 'port_list', + reportconfig: 'report_config', + reportconfigs: 'report_config', reportformat: 'report_format', reportformats: 'report_format', scanconfig: 'config', diff --git a/src/gmp/commands/__tests__/reportconfig.js b/src/gmp/commands/__tests__/reportconfig.js new file mode 100644 index 0000000000..fc8cda6a42 --- /dev/null +++ b/src/gmp/commands/__tests__/reportconfig.js @@ -0,0 +1,152 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { + createHttp, + createEntityResponse, + createActionResultResponse, +} from '../testing'; +import {ReportConfigCommand} from '../reportconfigs'; + +describe('ReportConfigCommand tests', () => { + test('should return single report config', () => { + const response = createEntityResponse('report_config', { + _id: 'foo', + }); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigCommand(fakeHttp); + return cmd.get({id: 'foo'}).then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('get', { + args: { + cmd: 'get_report_config', + report_config_id: 'foo', + }, + }); + const {data} = resp; + expect(data.id).toEqual('foo'); + }); + }); + + test('should create report config', () => { + const response = createActionResultResponse(); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigCommand(fakeHttp); + return cmd + .create({ + name: 'foo', + comment: 'bar', + report_format_id: 'baz', + params: { + 'param 1': 'value 1', + 'param 2': 'value 2', + 'param 3': ['report-format-1', 'report-format-2'], + }, + params_using_default: { + 'param 1': false, + 'param 2': true, + 'param 3': false, + }, + }) + .then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('post', { + data: { + cmd: 'create_report_config', + name: 'foo', + comment: 'bar', + report_format_id: 'baz', + 'param:param 1': 'value 1', + 'param:param 2': 'value 2', + 'param:param 3': 'report-format-1,report-format-2', + 'param_using_default:param 2': 1, + }, + }); + const {data} = resp; + expect(data.id).toEqual('foo'); + }); + }); + + test('should save report config', () => { + const response = createActionResultResponse(); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigCommand(fakeHttp); + return cmd + .save({ + name: 'foo', + comment: 'bar', + report_format_id: 'should-be-ignored-in-save', + params: { + 'param 1': 'value A', + 'param 2': 'value B', + 'param 3': ['report-format-A', 'report-format-B'], + }, + params_using_default: { + 'param 1': true, + 'param 2': false, + 'param 3': false, + }, + }) + .then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('post', { + data: { + cmd: 'save_report_config', + name: 'foo', + comment: 'bar', + 'param:param 1': 'value A', + 'param:param 2': 'value B', + 'param:param 3': 'report-format-A,report-format-B', + 'param_using_default:param 1': 1, + }, + }); + const {data} = resp; + expect(data.id).toEqual('foo'); + }); + }); + + test('should delete report config', () => { + const response = createActionResultResponse(); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigCommand(fakeHttp); + return cmd + .delete({ + id: 'foo', + }) + .then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('post', { + data: { + cmd: 'delete_report_config', + report_config_id: 'foo', + }, + }); + }); + }); +}); diff --git a/src/gmp/commands/__tests__/reportconfigs.js b/src/gmp/commands/__tests__/reportconfigs.js new file mode 100644 index 0000000000..1cd360b20c --- /dev/null +++ b/src/gmp/commands/__tests__/reportconfigs.js @@ -0,0 +1,102 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import {createHttp, createEntitiesResponse} from '../testing'; +import {ReportConfigsCommand} from '../reportconfigs'; +import {ALL_FILTER} from 'gmp/models/filter'; + +describe('ReportConfigsCommand tests', () => { + test('should return all report configs', () => { + const response = createEntitiesResponse('report_config', [ + { + _id: '1', + }, + { + _id: '2', + }, + ]); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigsCommand(fakeHttp); + return cmd.getAll().then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('get', { + args: { + cmd: 'get_report_configs', + filter: ALL_FILTER.toFilterString(), + }, + }); + const {data} = resp; + expect(data.length).toEqual(2); + }); + }); + + test('should return report configs', () => { + const response = createEntitiesResponse('report_config', [ + { + _id: '1', + }, + { + _id: '2', + }, + ]); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigsCommand(fakeHttp); + return cmd.get().then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('get', { + args: { + cmd: 'get_report_configs', + }, + }); + const {data} = resp; + expect(data.length).toEqual(2); + }); + }); + + test('should return filtered report configs', () => { + const response = createEntitiesResponse('report_config', [ + { + _id: '1', + }, + { + _id: '2', + }, + ]); + + const fakeHttp = createHttp(response); + + expect.hasAssertions(); + + const cmd = new ReportConfigsCommand(fakeHttp); + return cmd.get({filter: 'test filter'}).then(resp => { + expect(fakeHttp.request).toHaveBeenCalledWith('get', { + args: { + cmd: 'get_report_configs', + filter: 'test filter', + }, + }); + const {data} = resp; + expect(data.length).toEqual(2); + }); + }); +}); diff --git a/src/gmp/commands/reportconfigs.js b/src/gmp/commands/reportconfigs.js new file mode 100644 index 0000000000..a642deae66 --- /dev/null +++ b/src/gmp/commands/reportconfigs.js @@ -0,0 +1,121 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import logger from 'gmp/log'; + +import registerCommand from 'gmp/command'; + +import ReportConfig from 'gmp/models/reportconfig'; + +import {isArray} from 'gmp/utils/identity'; + +import EntitiesCommand from './entities'; +import EntityCommand from './entity'; +import {convertBoolean} from 'gmp/commands/convert'; + +const log = logger.getLogger('gmp.commands.reportconfigs'); + +export class ReportConfigCommand extends EntityCommand { + constructor(http) { + super(http, 'report_config', ReportConfig); + } + + create(args) { + const { + comment, + name, + report_format_id, + params = {}, + params_using_default = {}, + } = args; + + const data = { + cmd: 'create_report_config', + name, + comment, + report_format_id, + }; + + for (const prefname in params) { + let value = params[prefname]; + if (isArray(params[prefname])) { + value = params[prefname].join(','); + } + data['param:' + prefname] = value; + } + + for (const param_name in params_using_default) { + if (params_using_default[param_name]) { + data['param_using_default:' + param_name] = convertBoolean( + params_using_default[param_name], + ); + } + } + + log.debug('Creating new report config', args); + return this.action(data); + } + + save(args) { + const {id, comment, name, params = {}, params_using_default = {}} = args; + + const data = { + cmd: 'save_report_config', + id, + name, + comment, + }; + + for (const param_name in params_using_default) { + if (params_using_default[param_name]) { + data['param_using_default:' + param_name] = convertBoolean( + params_using_default[param_name], + ); + } + } + + for (const prefname in params) { + let value = params[prefname]; + if (isArray(params[prefname])) { + value = params[prefname].join(','); + } + data['param:' + prefname] = value; + } + + log.debug('Saving report config', args, data); + return this.action(data); + } + + getElementFromRoot(root) { + return root.get_report_config.get_report_configs_response.report_config; + } +} + +export class ReportConfigsCommand extends EntitiesCommand { + constructor(http) { + super(http, 'report_config', ReportConfig); + } + + getEntitiesResponse(root) { + return root.get_report_configs.get_report_configs_response; + } +} + +registerCommand('reportconfig', ReportConfigCommand); +registerCommand('reportconfigs', ReportConfigsCommand); + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/gmp/commands/trashcan.js b/src/gmp/commands/trashcan.js index b5cdbc6858..023679fbe9 100644 --- a/src/gmp/commands/trashcan.js +++ b/src/gmp/commands/trashcan.js @@ -30,6 +30,7 @@ import Note from 'gmp/models/note'; import Override from 'gmp/models/override'; import Permission from 'gmp/models/permission'; import PortList from 'gmp/models/portlist'; +import ReportConfig from 'gmp/models/reportconfig'; import ReportFormat from 'gmp/models/reportformat'; import Role from 'gmp/models/role'; import Scanner from 'gmp/models/scanner'; @@ -118,6 +119,12 @@ class Trashcan extends HttpCommand { model => PortList.fromElement(model), ); } + if (isDefined(trash_data.get_report_configs_response)) { + data.report_config_list = map( + trash_data.get_report_configs_response.report_config, + model => ReportConfig.fromElement(model), + ); + } if (isDefined(trash_data.get_report_formats_response)) { data.report_format_list = map( trash_data.get_report_formats_response.report_format, diff --git a/src/gmp/commands/users.js b/src/gmp/commands/users.js index 97862e6466..2870879216 100644 --- a/src/gmp/commands/users.js +++ b/src/gmp/commands/users.js @@ -66,6 +66,7 @@ export const DEFAULT_FILTER_SETTINGS = { portlist: '7d52d575-baeb-4d98-bb68-e1730dbc6236', report: '48ae588e-9085-41bc-abcb-3d6389cf7237', reportformat: '249c7a55-065c-47fb-b453-78e11a665565', + reportconfig: 'eca9738b-4339-4a3d-bd13-3c61173236ab', result: '739ab810-163d-11e3-9af6-406186ea4fc5', role: 'f38e673a-bcd1-11e2-a19a-406186ea4fc5', scanconfig: '1a9fbd91-0182-44cd-bc88-a13a9b3b1bef', diff --git a/src/gmp/gmp.js b/src/gmp/gmp.js index 844b0d5368..bdf0e0ba97 100644 --- a/src/gmp/gmp.js +++ b/src/gmp/gmp.js @@ -46,6 +46,7 @@ import 'gmp/commands/performance'; import 'gmp/commands/permissions'; import 'gmp/commands/policies'; import 'gmp/commands/portlists'; +import 'gmp/commands/reportconfigs'; import 'gmp/commands/reportformats'; import 'gmp/commands/reports'; import 'gmp/commands/results'; diff --git a/src/gmp/models/__tests__/reportconfig.js b/src/gmp/models/__tests__/reportconfig.js new file mode 100644 index 0000000000..f74def0e7d --- /dev/null +++ b/src/gmp/models/__tests__/reportconfig.js @@ -0,0 +1,503 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/* eslint-disable max-len */ + +import ReportConfig from 'gmp/models/reportconfig'; + +import {testModel} from 'gmp/models/testing'; + +testModel(ReportConfig, 'reportconfig', {testIsActive: false}); + +describe('Report Config model tests', () => { + test('should parse report format', () => { + const elem = { + report_format: { + _id: 'foo', + name: 'bar', + }, + }; + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.report_format.id).toEqual('foo'); + expect(reportConfig.report_format.name).toEqual('bar'); + }); + + test('should parse alerts', () => { + const elem = { + alerts: { + alert: { + _id: 'foo', + name: 'bar', + }, + }, + }; + const elem2 = { + alerts: { + alert: [ + { + _id: 'lorem', + name: 'ipsum', + }, + { + _id: 'foo', + name: 'bar', + }, + ], + }, + }; + const reportConfig = ReportConfig.fromElement(elem); + const reportConfig2 = ReportConfig.fromElement(elem2); + + expect(reportConfig.alerts[0].id).toEqual('foo'); + expect(reportConfig.alerts[0].name).toEqual('bar'); + + expect(reportConfig2.alerts[0].id).toEqual('lorem'); + expect(reportConfig2.alerts[0].name).toEqual('ipsum'); + expect(reportConfig2.alerts[1].id).toEqual('foo'); + expect(reportConfig2.alerts[1].name).toEqual('bar'); + }); + + describe('params tests', () => { + test('should parse params with attributes given as objects where applicable', () => { + const elem = { + param: [ + { + value: { + __text: 'lorem', + }, + default: { + __text: 'ipsum', + }, + name: 'foo', + type: { + __text: 'dolor', + max: '1', + min: '0', + }, + }, + ], + }; + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.param).toBeUndefined(); + expect(reportConfig.params[0].value).toEqual('lorem'); + expect(reportConfig.params[0].default).toEqual('ipsum'); + expect(reportConfig.params[0].name).toEqual('foo'); + expect(reportConfig.params[0].max).toEqual('1'); + expect(reportConfig.params[0].min).toEqual('0'); + expect(reportConfig.params[0].type).toEqual('dolor'); + }); + + test('should parse params with attributes not given as objects', () => { + const elem = { + param: [ + { + value: 'lorem', + default: 'ipsum', + name: 'foo', + type: { + max: '1', + min: '0', + }, + }, + ], + }; + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].value).toEqual('lorem'); + expect(reportConfig.params[0].default).toEqual('ipsum'); + expect(reportConfig.params[0].name).toEqual('foo'); + }); + + test('should parse options in params', () => { + const elem = { + param: [ + { + options: { + option: ['opt1', 'opt2'], + }, + type: { + max: '1', + min: '0', + }, + }, + ], + }; + const res = [ + {value: 'opt1', name: 'opt1'}, + {value: 'opt2', name: 'opt2'}, + ]; + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].options).toEqual(res); + }); + + test('should return empty array if no options are given', () => { + const elem = { + param: [ + { + type: { + max: '1', + min: '0', + }, + }, + ], + }; + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].options).toEqual([]); + }); + + test('should parse param if it is not in an array', () => { + const elem = { + param: { + name: 'foo', + type: { + max: '1', + min: '0', + }, + }, + }; + + const reportConfig = ReportConfig.fromElement(elem); + expect(reportConfig.params[0].name).toEqual('foo'); + }); + + test('should parse param value_using_default', () => { + const elem = { + param: [ + { + name: 'foo', + value: { + _using_default: '1', + }, + type: { + max: '1', + min: '0', + }, + }, + { + name: 'bar', + value: { + _using_default: '0', + }, + type: { + max: '1', + min: '0', + }, + }, + ], + }; + + const reportConfig = ReportConfig.fromElement(elem); + expect(reportConfig.params[0].name).toEqual('foo'); + expect(reportConfig.params[0].value_using_default).toEqual(true); + expect(reportConfig.params[1].name).toEqual('bar'); + expect(reportConfig.params[1].value_using_default).toEqual(false); + }); + + test('should parse value, default and type in string params', () => { + const elem = { + param: [ + { + value: { + _using_default: '0', + __text: 'foo', + }, + default: 'bar', + type: { + __text: 'string', + max: '1', + min: '0', + }, + }, + { + value: 'baz', + default: 'boo', + type: { + __text: 'string', + max: '1', + min: '0', + }, + }, + ], + }; + + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].type).toEqual('string'); + expect(reportConfig.params[0].value).toEqual('foo'); + expect(reportConfig.params[0].default).toEqual('bar'); + expect(reportConfig.params[0].value_using_default).toEqual(false); + + expect(reportConfig.params[1].type).toEqual('string'); + expect(reportConfig.params[1].value).toEqual('baz'); + expect(reportConfig.params[1].default).toEqual('boo'); + expect(reportConfig.params[0].value_using_default).toEqual(false); + }); + + test('should parse value, default and type in text params', () => { + const elem = { + param: [ + { + value: { + _using_default: '0', + __text: 'foo', + }, + default: 'bar', + type: { + __text: 'text', + max: '1', + min: '0', + }, + }, + { + value: 'baz', + default: 'boo', + type: { + __text: 'text', + max: '1', + min: '0', + }, + }, + ], + }; + + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].type).toEqual('text'); + expect(reportConfig.params[0].value).toEqual('foo'); + expect(reportConfig.params[0].default).toEqual('bar'); + expect(reportConfig.params[0].value_using_default).toEqual(false); + + expect(reportConfig.params[1].type).toEqual('text'); + expect(reportConfig.params[1].value).toEqual('baz'); + expect(reportConfig.params[1].default).toEqual('boo'); + expect(reportConfig.params[0].value_using_default).toEqual(false); + }); + + test('should parse value, default and type in integer params', () => { + const elem = { + param: [ + { + value: { + _using_default: '0', + __text: '123', + }, + default: '234', + type: { + __text: 'integer', + max: '999', + min: '0', + }, + }, + { + value: '345', + default: '456', + type: { + __text: 'integer', + max: '999', + min: '0', + }, + }, + ], + }; + + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].type).toEqual('integer'); + expect(reportConfig.params[0].value).toEqual(123); + expect(reportConfig.params[0].default).toEqual(234); + expect(reportConfig.params[0].value_using_default).toEqual(false); + + expect(reportConfig.params[1].type).toEqual('integer'); + expect(reportConfig.params[1].value).toEqual(345); + expect(reportConfig.params[1].default).toEqual(456); + expect(reportConfig.params[0].value_using_default).toEqual(false); + }); + + test('should parse value, default and type in boolean params', () => { + const elem = { + param: [ + { + value: { + _using_default: '0', + __text: '0', + }, + default: '1', + type: { + __text: 'boolean', + max: '1', + min: '0', + }, + }, + { + value: '1', + default: '1', + type: { + __text: 'boolean', + max: '1', + min: '0', + }, + }, + ], + }; + + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].type).toEqual('boolean'); + expect(reportConfig.params[0].value).toEqual(false); + expect(reportConfig.params[0].default).toEqual(true); + expect(reportConfig.params[0].value_using_default).toEqual(false); + + expect(reportConfig.params[1].type).toEqual('boolean'); + expect(reportConfig.params[1].value).toEqual(true); + expect(reportConfig.params[1].default).toEqual(true); + expect(reportConfig.params[0].value_using_default).toEqual(false); + }); + + test('should parse options, value, default and type in selection params', () => { + const elem = { + param: [ + { + value: { + _using_default: '0', + __text: 'opt1', + }, + default: 'opt2', + options: { + option: ['opt1', 'opt2'], + }, + type: { + __text: 'selection', + max: '1', + min: '0', + }, + }, + { + value: 'optA', + default: 'optA', + options: { + option: ['optA', 'optB'], + }, + type: { + __text: 'selection', + max: '1', + min: '0', + }, + }, + ], + }; + + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].type).toEqual('selection'); + expect(reportConfig.params[0].value).toEqual('opt1'); + expect(reportConfig.params[0].default).toEqual('opt2'); + const expectedOptions0 = [ + { + value: 'opt1', + name: 'opt1', + }, + { + value: 'opt2', + name: 'opt2', + }, + ]; + expect(reportConfig.params[0].options).toEqual(expectedOptions0); + + expect(reportConfig.params[1].type).toEqual('selection'); + expect(reportConfig.params[1].value).toEqual('optA'); + expect(reportConfig.params[1].default).toEqual('optA'); + const expectedOptions1 = [ + { + value: 'optA', + name: 'optA', + }, + { + value: 'optB', + name: 'optB', + }, + ]; + expect(reportConfig.params[1].options).toEqual(expectedOptions1); + }); + + test('should parse value, default and type in report_format_list params', () => { + const elem = { + param: [ + { + type: { + __text: 'report_format_list', + max: '1', + min: '0', + }, + value: { + _using_default: '0', + report_format: [ + {_id: '42', name: 'ABC'}, + {_id: '21', name: 'DEF'}, + ], + }, + default: { + report_format: [ + {_id: '12', name: 'GHI'}, + {_id: '34', name: 'JKL'}, + ], + }, + }, + { + type: { + __text: 'report_format_list', + max: '1', + min: '0', + }, + value: { + _using_default: '0', + report_format: {_id: '123', name: 'XYZ'}, + }, + default: { + report_format: {_id: '456', name: 'UVW'}, + }, + }, + ], + }; + const reportConfig = ReportConfig.fromElement(elem); + + expect(reportConfig.params[0].type).toEqual('report_format_list'); + expect(reportConfig.params[0].value).toEqual(['42', '21']); + expect(reportConfig.params[0].value_labels).toEqual({ + 42: 'ABC', + 21: 'DEF', + }); + expect(reportConfig.params[0].default).toEqual(['12', '34']); + expect(reportConfig.params[0].default_labels).toEqual({ + 12: 'GHI', + 34: 'JKL', + }); + expect(reportConfig.params[0].value_using_default).toEqual(false); + + expect(reportConfig.params[1].type).toEqual('report_format_list'); + expect(reportConfig.params[1].value).toEqual(['123']); + expect(reportConfig.params[1].value_labels).toEqual({123: 'XYZ'}); + expect(reportConfig.params[1].default).toEqual(['456']); + expect(reportConfig.params[1].default_labels).toEqual({456: 'UVW'}); + expect(reportConfig.params[0].value_using_default).toEqual(false); + }); + }); +}); diff --git a/src/gmp/models/__tests__/reportformat.js b/src/gmp/models/__tests__/reportformat.js index 20b8c8a74c..81d44037bc 100644 --- a/src/gmp/models/__tests__/reportformat.js +++ b/src/gmp/models/__tests__/reportformat.js @@ -64,6 +64,16 @@ describe('ReportFormat model tests', () => { expect(reportFormat2.active).toEqual(YES_VALUE); }); + test('should parse configurable as boolean correctly', () => { + const reportFormat = ReportFormat.fromElement({configurable: '0'}); + const reportFormat2 = ReportFormat.fromElement({configurable: '1'}); + const reportFormat3 = ReportFormat.fromElement({}); + + expect(reportFormat.configurable).toEqual(false); + expect(reportFormat2.configurable).toEqual(true); + expect(reportFormat3.configurable).toEqual(false); + }); + test('should parse predefined as boolean correctly', () => { const reportFormat = ReportFormat.fromElement({predefined: '0'}); const reportFormat2 = ReportFormat.fromElement({predefined: '1'}); @@ -91,6 +101,15 @@ describe('ReportFormat model tests', () => { expect(reportFormat.alerts).toEqual([]); }); + test('should parse invisible alerts count', () => { + const elem = { + invisible_alerts: '3', + }; + const reportFormat = ReportFormat.fromElement(elem); + + expect(reportFormat.invisible_alerts).toEqual(3); + }); + describe('params tests', () => { test('should parse params with attributes given as objects where applicable', () => { const elem = { @@ -141,12 +160,12 @@ describe('ReportFormat model tests', () => { const elem = { param: [ { + options: { + option: ['opt1', 'opt2'], + }, type: { max: '1', min: '0', - options: { - option: ['opt1', 'opt2'], - }, }, }, ], diff --git a/src/gmp/models/filter.js b/src/gmp/models/filter.js index 6c8e8c3b24..b884ceec00 100644 --- a/src/gmp/models/filter.js +++ b/src/gmp/models/filter.js @@ -794,6 +794,8 @@ export const OS_FILTER_FILTER = Filter.fromString('type=os'); export const OVERRIDES_FILTER_FILTER = Filter.fromString('type=override'); export const PORTLISTS_FILTER_FILTER = Filter.fromString('type=port_list'); export const PERMISSIONS_FILTER_FILTER = Filter.fromString('type=permission'); +export const REPORT_CONFIGS_FILTER_FILTER = + Filter.fromString('type=report_config'); export const REPORT_FORMATS_FILTER_FILTER = Filter.fromString('type=report_format'); export const REPORTS_FILTER_FILTER = Filter.fromString('type=report'); diff --git a/src/gmp/models/reportconfig.js b/src/gmp/models/reportconfig.js new file mode 100644 index 0000000000..c43e602d96 --- /dev/null +++ b/src/gmp/models/reportconfig.js @@ -0,0 +1,107 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import {isDefined, isObject} from 'gmp/utils/identity'; +import {forEach, map} from 'gmp/utils/array'; + +import {parseBoolean} from 'gmp/parser'; + +import Model, {parseModelFromElement} from 'gmp/model'; + +const get_value = val => { + return isObject(val) ? val.__text : val; +}; + +class Param { + constructor({name, type, value, options, _using_default, ...other}) { + this.name = name; + this.max = type.max; + this.min = type.min; + this.type = get_value(type); + this.value_using_default = parseBoolean(value?._using_default); + + if (isObject(options)) { + this.options = map(options.option, opt => { + return { + value: opt, + name: opt, + }; + }); + } else { + this.options = []; + } + + if (this.type === 'report_format_list') { + this.value = map(value.report_format, format => format._id); + this.default = map(other.default.report_format, format => format._id); + this.value_labels = {}; + this.default_labels = {}; + forEach(value.report_format, format => { + this.value_labels[format._id] = format.name; + }); + forEach(other.default.report_format, format => { + this.default_labels[format._id] = format.name; + }); + } else if (this.type === 'integer') { + this.value = parseInt(get_value(value)); + this.default = parseInt(get_value(other.default)); + } else if (this.type === 'boolean') { + this.value = parseBoolean(get_value(value)); + this.default = parseBoolean(get_value(other.default)); + } else { + this.value = get_value(value); + this.default = get_value(other.default); + } + } +} + +class ReportConfig extends Model { + static entityType = 'reportconfig'; + + static parseElement(element) { + const ret = super.parseElement(element); + + if (isDefined(ret.report_format)) { + ret.report_format = { + id: ret.report_format._id, + name: ret.report_format.name, + }; + } else { + ret.report_format = {}; + } + + ret.params = map(ret.param, param => { + return new Param(param); + }); + + delete ret.param; + + if (isDefined(ret.alerts)) { + ret.alerts = map(ret.alerts.alert, alert => + parseModelFromElement(alert, 'alert'), + ); + } else { + ret.alerts = []; + } + + return ret; + } +} + +export default ReportConfig; + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/gmp/models/reportformat.js b/src/gmp/models/reportformat.js index 6535d89b35..b74363873f 100644 --- a/src/gmp/models/reportformat.js +++ b/src/gmp/models/reportformat.js @@ -28,15 +28,15 @@ const get_value = val => { }; class Param { - constructor({name, type, value, ...other}) { + constructor({name, type, value, options, ...other}) { this.default = get_value(other.default); this.name = name; this.max = type.max; this.min = type.min; this.type = get_value(type); - if (isObject(type.options)) { - this.options = map(type.options.option, opt => { + if (isObject(options)) { + this.options = map(options.option, opt => { return { value: opt, name: opt, @@ -82,8 +82,20 @@ class ReportFormat extends Model { } else { ret.alerts = []; } + ret.invisible_alerts = parseInt(ret.invisible_alerts); + + if (isDefined(ret.report_configs)) { + ret.report_configs = map( + ret.report_configs.report_config, + report_config => parseModelFromElement(report_config, 'reportconfig'), + ); + } else { + ret.report_configs = []; + } + ret.invisible_report_configs = parseInt(ret.report_configs); ret.active = parseYesNo(element.active); + ret.configurable = parseBoolean(element.configurable); ret.predefined = parseBoolean(element.predefined); return ret; diff --git a/src/gmp/utils/__tests__/entitytype.js b/src/gmp/utils/__tests__/entitytype.js index f273e39f14..2c1e0f823b 100644 --- a/src/gmp/utils/__tests__/entitytype.js +++ b/src/gmp/utils/__tests__/entitytype.js @@ -85,6 +85,7 @@ describe('normalizeType function tests', () => { expect(normalizeType('dfn_cert_adv')).toEqual('dfncert'); expect(normalizeType('port_list')).toEqual('portlist'); expect(normalizeType('port_range')).toEqual('portrange'); + expect(normalizeType('report_config')).toEqual('reportconfig'); expect(normalizeType('report_format')).toEqual('reportformat'); expect(normalizeType('config')).toEqual('scanconfig'); expect(normalizeType('vuln')).toEqual('vulnerability'); @@ -109,6 +110,7 @@ describe('apiType function tests', () => { expect(apiType('dfncert')).toEqual('dfn_cert_adv'); expect(apiType('portlist')).toEqual('port_list'); expect(apiType('portrange')).toEqual('port_range'); + expect(apiType('reportconfig')).toEqual('report_config'); expect(apiType('reportformat')).toEqual('report_format'); expect(apiType('scanconfig')).toEqual('config'); expect(apiType('vulnerability')).toEqual('vuln'); @@ -139,6 +141,7 @@ describe('typeName function tests', () => { expect(typeName('dfncert')).toEqual('DFN-CERT Advisory'); expect(typeName('portlist')).toEqual('Port List'); expect(typeName('portrange')).toEqual('Port Range'); + expect(typeName('reportconfig')).toEqual('Report Config'); expect(typeName('scanconfig')).toEqual('Scan Config'); expect(typeName('config')).toEqual('Scan Config'); expect(typeName('vulnerability')).toEqual('Vulnerability'); diff --git a/src/gmp/utils/entitytype.js b/src/gmp/utils/entitytype.js index 55c363f912..09aba22f6b 100644 --- a/src/gmp/utils/entitytype.js +++ b/src/gmp/utils/entitytype.js @@ -52,6 +52,7 @@ const TYPES = { os: 'operatingsystem', port_list: 'portlist', port_range: 'portrange', + report_config: 'reportconfig', report_format: 'reportformat', tls_certificate: 'tlscertificate', vuln: 'vulnerability', @@ -89,6 +90,7 @@ const ENTITY_TYPES = { portlist: _l('Port List'), portrange: _l('Port Range'), report: _l('Report'), + reportconfig: _l('Report Config'), reportformat: _l('Report Format'), result: _l('Result'), role: _l('Role'), @@ -124,6 +126,7 @@ const CMD_TYPES = { operatingsystem: 'os', portlist: 'port_list', portrange: 'port_range', + reportconfig: 'report_config', reportformat: 'report_format', tlscertificate: 'tls_certificate', vulnerability: 'vuln', diff --git a/src/web/components/bar/menubar.js b/src/web/components/bar/menubar.js index 0c94f70b9d..ea33f47a71 100644 --- a/src/web/components/bar/menubar.js +++ b/src/web/components/bar/menubar.js @@ -87,6 +87,7 @@ const MenuBar = ({isLoggedIn, capabilities}) => { 'scan_configs', 'alerts', 'schedules', + 'report_configs', 'report_formats', 'scanners', 'filters', @@ -213,6 +214,9 @@ const MenuBar = ({isLoggedIn, capabilities}) => { {capabilities.mayAccess('schedules') && ( )} + {capabilities.mayAccess('report_configs') && ( + + )} {capabilities.mayAccess('report_formats') && ( )} diff --git a/src/web/components/icon/reportconfigicon.js b/src/web/components/icon/reportconfigicon.js new file mode 100644 index 0000000000..373df417c8 --- /dev/null +++ b/src/web/components/icon/reportconfigicon.js @@ -0,0 +1,26 @@ +/* Copyright (C) 2017-2022 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import withSvgIcon from './withSvgIcon'; + +import {ReactComponent as Icon} from './svg/report_format.svg'; + +const ReportConfigIcon = withSvgIcon()(Icon); + +export default ReportConfigIcon; + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/extras/trashactions.js b/src/web/pages/extras/trashactions.js index 6c64fd9295..980be99fa0 100644 --- a/src/web/pages/extras/trashactions.js +++ b/src/web/pages/extras/trashactions.js @@ -65,6 +65,9 @@ const getRestorableDeletableForEntityType = { portlist: entity => { return {restorable: true, deletable: !entity.isInUse()}; }, + reportconfig: entity => { + return {restorable: true, deletable: !entity.isInUse()}; + }, reportformat: entity => { return {restorable: true, deletable: !entity.isInUse()}; }, diff --git a/src/web/pages/extras/trashcanpage.js b/src/web/pages/extras/trashcanpage.js index bea00141af..bde4f40774 100644 --- a/src/web/pages/extras/trashcanpage.js +++ b/src/web/pages/extras/trashcanpage.js @@ -66,6 +66,7 @@ import OverridesTable from '../overrides/table'; import PermissionsTable from '../permissions/table'; import PoliciesTable from '../policies/table'; import PortListsTable from '../portlists/table'; +import ReportConfigsTable from '../reportconfigs/table'; import ReportFormatsTable from '../reportformats/table'; import RolesTable from '../roles/table'; import ScannersTable from '../scanners/table'; @@ -237,6 +238,7 @@ class Trashcan extends React.Component { const render_overrides = isDefined(trash.override_list); const render_permissions = isDefined(trash.permission_list); const render_port_lists = isDefined(trash.port_list_list); + const render_report_configs = isDefined(trash.report_config_list); const render_report_formats = isDefined(trash.report_format_list); const render_roles = isDefined(trash.role_list); const render_scanners = isDefined(trash.scanner_list); @@ -299,6 +301,12 @@ class Trashcan extends React.Component { _('Port Lists'), trash.port_list_list.length, )} + {render_report_configs && + this.createContentRow( + 'report_config', + _('Report Configs'), + trash.report_config_list.length, + )} {render_report_formats && this.createContentRow( 'report_format', @@ -481,6 +489,16 @@ class Trashcan extends React.Component { /> )} + {isDefined(trash.report_config_list) && ( + + +

{_('Report Configs')}

+ +
+ )} {isDefined(trash.report_format_list) && ( diff --git a/src/web/pages/filters/component.js b/src/web/pages/filters/component.js index 2a566f4886..7e78ce894e 100644 --- a/src/web/pages/filters/component.js +++ b/src/web/pages/filters/component.js @@ -42,6 +42,7 @@ const FILTER_OPTIONS = [ ['permission', _l('Permission')], ['port_list', _l('Port List')], ['report', _l('Report')], + ['report_config', _l('Report Config')], ['report_format', _l('Report Format')], ['result', _l('Result')], ['role', _l('Role')], @@ -162,16 +163,8 @@ class FilterComponent extends React.Component { onSaveError, } = this.props; - const { - comment, - dialogVisible, - id, - name, - term, - title, - type, - types, - } = this.state; + const {comment, dialogVisible, id, name, term, title, type, types} = + this.state; return ( . + */ + +export const mockReportConfig = { + _id: '12345', + name: 'foo', + comment: 'bar', + creation_time: '2019-07-16T06:31:29Z', + modification_time: '2019-07-16T06:44:55Z', + owner: {name: 'admin'}, + writable: '1', + in_use: '0', + report_format: { + _id: '123456', + name: 'example-configurable-1', + }, + param: [ + { + name: 'StringParam', + value: { + __text: 'StringValue', + _using_default: '1', + }, + default: 'StringValue', + type: { + __text: 'string', + min: '0', + max: '100', + }, + }, + { + name: 'TextParam', + value: { + __text: 'TextValue', + _using_default: '0', + }, + default: 'TextDefault', + type: { + __text: 'text', + min: '0', + max: '1000', + }, + }, + { + name: 'IntegerParam', + value: { + __text: '12', + _using_default: '1', + }, + default: '12', + type: { + __text: 'integer', + min: 0, + max: 50, + }, + }, + { + name: 'BooleanParam', + value: { + __text: '1', + _using_default: '0', + }, + default: '0', + type: { + __text: 'boolean', + min: 0, + max: 1, + }, + }, + { + name: 'SelectionParam', + value: { + __text: 'OptionB', + _using_default: '0', + }, + default: 'OptionA', + options: { + option: ['OptionA', 'OptionB', 'OptionC'], + }, + type: { + __text: 'selection', + min: 0, + max: 1, + }, + }, + { + name: 'ReportFormatListParam', + value: { + __text: '654321,7654321', + report_format: [ + { + _id: '654321', + name: 'non-configurable-1', + }, + { + _id: '7654321', + name: 'non-configurable-2', + }, + ], + _using_default: '0', + }, + default: { + __text: '7654321,1234567', + report_format: [ + { + _id: '7654321', + name: 'non-configurable-2', + }, + { + _id: '1234567', + name: 'example-configurable-2', + }, + ], + }, + type: { + __text: 'report_format_list', + min: 0, + max: 1, + }, + }, + ], + alerts: { + alert: [ + { + _id: '321', + name: 'ABC', + }, + { + _id: '789', + name: 'XYZ', + }, + ], + }, +}; diff --git a/src/web/pages/reportconfigs/__mocks__/mockreportformats.js b/src/web/pages/reportconfigs/__mocks__/mockreportformats.js new file mode 100644 index 0000000000..3040d62c63 --- /dev/null +++ b/src/web/pages/reportconfigs/__mocks__/mockreportformats.js @@ -0,0 +1,40 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +export const mockReportFormats = [ + { + id: '123456', + name: 'example-configurable-1', + configurable: true, + }, + { + id: '654321', + name: 'non-configurable-1', + configurable: false, + }, + { + id: '1234567', + name: 'example-configurable-2', + configurable: true, + }, + { + id: '7654321', + name: 'non-configurable-2', + configurable: false, + }, +]; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/component.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/component.js.snap new file mode 100644 index 0000000000..f6a20df465 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/component.js.snap @@ -0,0 +1,1732 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Report Config Component tests should open create dialog and call GMP create 1`] = ` +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c23 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c26 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c1 { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin: 10% auto; + border: 0; + outline: 0; + width: 800px; + height: px; +} + +.c0 { + position: fixed; + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: 0; + background: rgba(102, 102, 102, 0.5); + z-index: 600; + -webkit-transition: opacity 1s ease-in; + transition: opacity 1s ease-in; + width: 100%; + height: 100%; +} + +.c33 { + width: 0; + height: 0; + cursor: nwse-resize; + border-right: 20px solid transparent; + border-bottom: 20px solid transparent; + border-top: 20px solid #fff; +} + +.c32 { + position: absolute; + bottom: 3px; + right: 3px; + width: 20px; + height: 20px; + background: repeating-linear-gradient( -45deg, transparent, transparent 2px, #000 2px, #000 3px ); +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: inherit; + padding: 0; + background: #fff; + box-shadow: 5px 5px 10px #7F7F7F; + border-radius: 3px; + border: 1px solid #7F7F7F; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-weight: bold; + font-size: 12px; + font-family: Verdana,sans-serif; + color: #074320; + cursor: pointer; + border-radius: 2px; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.c5 :hover { + border: 1px solid #074320; +} + +.c6 { + height: 24px; + width: 24px; + line-height: 24px; +} + +.c6 * { + height: inherit; + width: inherit; +} + +.c29 { + display: inline-block; + padding: 0 15px; + color: #4C4C4C; + text-align: center; + vertical-align: middle; + font-size: 11px; + font-weight: bold; + line-height: 30px; + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; + background-color: #fff; + border-radius: 2px; + border: 1px solid #bfbfbf; + cursor: pointer; + overflow: visible; + z-index: 1; +} + +.c29:focus, +.c29:hover { + border: 1px solid #4C4C4C; +} + +.c29:hover { + -webkit-text-decoration: none; + text-decoration: none; + background: #11ab51; + font-weight: bold; + color: #fff; +} + +.c29[disabled] { + cursor: not-allowed; + opacity: 0.65; + box-shadow: none; +} + +.c29 img { + height: 32px; + width: 32px; + margin-top: 5px 10px 5px -10px; + vertical-align: middle; +} + +.c29:link { + -webkit-text-decoration: none; + text-decoration: none; + color: #4C4C4C; +} + +.c30 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c31 { + border: 1px solid #7F7F7F; + color: #fff; + background: #11ab51; +} + +.c31 :hover { + color: #074320; + background: #A1DDBA; +} + +.c27 { + border-width: 1px 0 0 0; + border-style: solid; + border-color: #e5e5e5; + margin-top: 15px; + padding: 10px 20px 10px 20px; +} + +.c28 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c4 { + padding: 5px 5px 5px 10px; + margin-bottom: 15px; + border-radius: 2px 2px 0 0; + border-bottom: 1px solid #7F7F7F; + color: #fff; + font-weight: bold; + background: #11ab51; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + cursor: -webkit-grab; + cursor: grab; +} + +.c9 { + overflow: auto; + padding: 0 15px; + width: 100%; + height: 100%; + max-height: 400px; +} + +.c8 { + overflow: hidden; + height: 100%; +} + +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + padding-bottom: 10px; +} + +.c12 { + display: inline-block; + max-width: 100%; + font-weight: bold; + text-align: right; + padding-left: 10px; + padding-right: 10px; + width: 16.66666667%; + margin-left: 0; +} + +.c14 { + width: 83.33333333%; + padding-left: 10px; + padding-right: 10px; +} + +.c15 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; +} + +.c15:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c16 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c17 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + +.c24 { + background-color: transparent; + border: none; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + outline: none; + margin: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; +} + +.c25 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c25 * { + height: inherit; + width: inherit; +} + +.c20 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; +} + +.c19 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 180px; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: pointer; +} + +.c22 { + cursor: default; +} + +.c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + + +
+
+
+ +
+
+ +`; + +exports[`Report Config Component tests should open edit dialog and call GMP save 1`] = ` +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c23 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c37 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c34 { + margin-left: -5px; +} + +.c34>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c34>* { + margin-left: 5px; +} + +.c33 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c1 { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin: 10% auto; + border: 0; + outline: 0; + width: 800px; + height: px; +} + +.c0 { + position: fixed; + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: 0; + background: rgba(102, 102, 102, 0.5); + z-index: 600; + -webkit-transition: opacity 1s ease-in; + transition: opacity 1s ease-in; + width: 100%; + height: 100%; +} + +.c44 { + width: 0; + height: 0; + cursor: nwse-resize; + border-right: 20px solid transparent; + border-bottom: 20px solid transparent; + border-top: 20px solid #fff; +} + +.c43 { + position: absolute; + bottom: 3px; + right: 3px; + width: 20px; + height: 20px; + background: repeating-linear-gradient( -45deg, transparent, transparent 2px, #000 2px, #000 3px ); +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: inherit; + padding: 0; + background: #fff; + box-shadow: 5px 5px 10px #7F7F7F; + border-radius: 3px; + border: 1px solid #7F7F7F; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-weight: bold; + font-size: 12px; + font-family: Verdana,sans-serif; + color: #074320; + cursor: pointer; + border-radius: 2px; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.c5 :hover { + border: 1px solid #074320; +} + +.c6 { + height: 24px; + width: 24px; + line-height: 24px; +} + +.c6 * { + height: inherit; + width: inherit; +} + +.c40 { + display: inline-block; + padding: 0 15px; + color: #4C4C4C; + text-align: center; + vertical-align: middle; + font-size: 11px; + font-weight: bold; + line-height: 30px; + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; + background-color: #fff; + border-radius: 2px; + border: 1px solid #bfbfbf; + cursor: pointer; + overflow: visible; + z-index: 1; +} + +.c40:focus, +.c40:hover { + border: 1px solid #4C4C4C; +} + +.c40:hover { + -webkit-text-decoration: none; + text-decoration: none; + background: #11ab51; + font-weight: bold; + color: #fff; +} + +.c40[disabled] { + cursor: not-allowed; + opacity: 0.65; + box-shadow: none; +} + +.c40 img { + height: 32px; + width: 32px; + margin-top: 5px 10px 5px -10px; + vertical-align: middle; +} + +.c40:link { + -webkit-text-decoration: none; + text-decoration: none; + color: #4C4C4C; +} + +.c41 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c42 { + border: 1px solid #7F7F7F; + color: #fff; + background: #11ab51; +} + +.c42 :hover { + color: #074320; + background: #A1DDBA; +} + +.c38 { + border-width: 1px 0 0 0; + border-style: solid; + border-color: #e5e5e5; + margin-top: 15px; + padding: 10px 20px 10px 20px; +} + +.c39 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c4 { + padding: 5px 5px 5px 10px; + margin-bottom: 15px; + border-radius: 2px 2px 0 0; + border-bottom: 1px solid #7F7F7F; + color: #fff; + font-weight: bold; + background: #11ab51; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + cursor: -webkit-grab; + cursor: grab; +} + +.c9 { + overflow: auto; + padding: 0 15px; + width: 100%; + height: 100%; + max-height: 400px; +} + +.c8 { + overflow: hidden; + height: 100%; +} + +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + padding-bottom: 10px; +} + +.c12 { + display: inline-block; + max-width: 100%; + font-weight: bold; + text-align: right; + padding-left: 10px; + padding-right: 10px; + width: 16.66666667%; + margin-left: 0; +} + +.c14 { + width: 83.33333333%; + padding-left: 10px; + padding-right: 10px; +} + +.c15 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; +} + +.c15:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c16 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c31 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c17 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + +.c24 { + background-color: transparent; + border: none; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + outline: none; + margin: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; +} + +.c25 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c25 * { + height: inherit; + width: inherit; +} + +.c20 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; + background-color: #f3f3f3; +} + +.c19 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 180px; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: default; +} + +.c22 { + cursor: default; +} + +.c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c32 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + font-weight: normal; + cursor: pointer; +} + +.c35 { + font-family: inherit; + font-size: inherit; + padding: 0; + margin: 0; + margin-left: 10px; + line-height: normal; + width: auto; + height: auto; +} + +.c26 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; + width: 100%; +} + +.c27 a { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; +} + +.c27 a:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #000; +} + +.c28 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 25%; +} + +.c29 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 70%; +} + +.c30 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 10%; +} + +.c36 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +@media print { + .c26 { + border-collapse: collapse; + } +} + +@media print { + .c27 { + border-bottom: 1px solid black; + } + + .c27 a, + .c27 a:hover { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; + } +} + +@media print { + .c28 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c29 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c30 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + + +
+
+
+ +
+
+
+ +`; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/details.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/details.js.snap new file mode 100644 index 0000000000..1c3ee5ab02 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/details.js.snap @@ -0,0 +1,497 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Report Config Details tests should render full Details 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c6 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c1 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; +} + +.c7 { + vertical-align: top; +} + +.c2 td { + padding: 4px 4px 4px 0; +} + +.c2 tr td:first-child { + padding-right: 5px; +} + +.c3 { + width: 10%; +} + +.c4 { + width: 90%; +} + +@media print { + .c1 { + border-collapse: collapse; + } +} + +
+ + + + + + + + + + + + + + + + + + + +
+
+ Report Format +
+
+ +
+
+ Parameters +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ StringParam +
+
+
+ StringValue +
+
+
+ TextParam +
+
+
+
+                          TextValue
+                        
+
+
+
+ IntegerParam +
+
+
+ 12 +
+
+
+ BooleanParam +
+
+
+ Yes +
+
+
+ SelectionParam +
+
+
+ OptionB +
+
+
+ ReportFormatListParam +
+
+ +
+
+
+
+
+ Alerts using this Report Config +
+
+
+ + + ABC + + + + + XYZ + + +
+
+
+`; + +exports[`Report Config Details tests should render orphaned config details 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c1 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; +} + +.c6 { + vertical-align: top; +} + +.c2 td { + padding: 4px 4px 4px 0; +} + +.c2 tr td:first-child { + padding-right: 5px; +} + +.c3 { + width: 10%; +} + +.c4 { + width: 90%; +} + +@media print { + .c1 { + border-collapse: collapse; + } +} + +
+ + + + + + + + + + + + + + + +
+
+ Report Format +
+
+
+ +
+
+
+ Parameters +
+
+
+
+ + + + + + +
+
+ + not available for orphaned report configs + +
+
+
+
+
+
+`; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/detailspage.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/detailspage.js.snap new file mode 100644 index 0000000000..ea9720d008 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/detailspage.js.snap @@ -0,0 +1,940 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Report Config Details Page tests should render full Details page with param details 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; +} + +.c1 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c14 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c19 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c22 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c24 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c27 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c29 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c7 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c4 { + margin-left: -10px; +} + +.c4>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c4>* { + margin-left: 10px; +} + +.c5 { + margin-left: -5px; +} + +.c5>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c5>* { + margin-left: 5px; +} + +.c3 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c8 { + cursor: pointer; +} + +.c9 svg path { + fill: #bfbfbf; +} + +.c6 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c6 * { + height: inherit; + width: inherit; +} + +.c16 { + height: 50px; + width: 50px; + line-height: 50px; +} + +.c16 * { + height: inherit; + width: inherit; +} + +.c11 { + margin: 10px 0px; + padding-bottom: 1px; + border-bottom: 2px solid #e5e5e5; + position: relative; +} + +.c12 { + margin: 0 0 1px 0; +} + +.c13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c15 { + margin-right: 5px; +} + +.c17 { + word-break: break-all; + min-width: 100px; +} + +.c25 { + font-size: 16px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: start; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: start; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding-left: 8px; + padding-right: 8px; + padding-bottom: 2px; + padding-top: 2px; + border-left: 1px solid #f3f3f3; + border-right: 1px solid #e5e5e5; + cursor: pointer; + background-color: #f3f3f3; + border-bottom: 1px solid #f3f3f3; + margin-bottom: -2px; + border-top: 2px solid #11ab51; +} + +.c25:hover { + border-top: 2px solid #11ab51; +} + +.c25:first-child { + border-left: 1px solid #e5e5e5; +} + +.c26 { + font-size: 16px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: start; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: start; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding-left: 8px; + padding-right: 8px; + padding-bottom: 2px; + padding-top: 2px; + border-left: 1px solid #fff; + border-right: 1px solid #e5e5e5; + cursor: pointer; + border-top: 2px solid #fff; +} + +.c26:hover { + border-top: 2px solid #e5e5e5; +} + +.c26:first-child { + border-left: 1px solid #fff; +} + +.c23 { + border-bottom: 2px solid #11ab51; + margin-top: 30px; + margin-bottom: 15px; +} + +.c30 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; +} + +.c34 { + vertical-align: top; +} + +.c31 td { + padding: 4px 4px 4px 0; +} + +.c31 tr td:first-child { + padding-right: 5px; +} + +.c20 { + border-spacing: 0px; + color: #7F7F7F; + font-size: 10px; +} + +.c20 :nth-child(even) { + margin-left: 3px; +} + +.c20 :nth-child(odd) { + margin-left: 30px; +} + +.c32 { + width: 10%; +} + +.c33 { + width: 90%; +} + +.c28 { + font-size: 0.7em; +} + +@media print { + .c8 { + display: none; + } +} + +@media print { + .c30 { + border-collapse: collapse; + } +} + +
+
+
+
+ +
+
+ + + new.svg + + + + + clone.svg + + + + + edit.svg + + + + + trashcan.svg + + +
+
+
+
+
+
+
+

+
+ + + report_format.svg + + +
+
+ Report Config: foo +
+

+
+
+
+
+ ID: +
+
+ 12345 +
+
+ Created: +
+
+ Tue, Jul 16, 2019 8:31 AM CEST +
+
+ Modified: +
+
+ Tue, Jul 16, 2019 8:44 AM CEST +
+
+ Owner: +
+ + admin + +
+
+
+
+
+
+
+
+ Information +
+
+
+ + Parameter Details + + + ( + + 6 + + ) + +
+
+
+
+ + User Tags + + + ( + + 0 + + ) + +
+
+
+
+ + Permissions + + + ( + + 0 + + ) + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+ Report Format +
+
+ +
+
+ Parameters +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ StringParam +
+
+
+ StringValue +
+
+
+ TextParam +
+
+
+
+                                  TextValue
+                                
+
+
+
+ IntegerParam +
+
+
+ 12 +
+
+
+ BooleanParam +
+
+
+ Yes +
+
+
+ SelectionParam +
+
+
+ OptionB +
+
+
+ ReportFormatListParam +
+
+ +
+
+
+
+
+ Alerts using this Report Config +
+
+
+ + + ABC + + + + + XYZ + + +
+
+
+
+
+
+
+`; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/dialog.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/dialog.js.snap new file mode 100644 index 0000000000..6b59f21618 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/dialog.js.snap @@ -0,0 +1,2394 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Edit Report Config Dialog component tests should render dialog with disabled report format selection 1`] = ` +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c23 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c54 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c50 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-flex-wrap: wrap; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c34 { + margin-left: -5px; +} + +.c34>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c34>* { + margin-left: 5px; +} + +.c33 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c1 { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin: 10% auto; + border: 0; + outline: 0; + width: 800px; + height: px; +} + +.c0 { + position: fixed; + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: 0; + background: rgba(102, 102, 102, 0.5); + z-index: 600; + -webkit-transition: opacity 1s ease-in; + transition: opacity 1s ease-in; + width: 100%; + height: 100%; +} + +.c61 { + width: 0; + height: 0; + cursor: nwse-resize; + border-right: 20px solid transparent; + border-bottom: 20px solid transparent; + border-top: 20px solid #fff; +} + +.c60 { + position: absolute; + bottom: 3px; + right: 3px; + width: 20px; + height: 20px; + background: repeating-linear-gradient( -45deg, transparent, transparent 2px, #000 2px, #000 3px ); +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: inherit; + padding: 0; + background: #fff; + box-shadow: 5px 5px 10px #7F7F7F; + border-radius: 3px; + border: 1px solid #7F7F7F; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-weight: bold; + font-size: 12px; + font-family: Verdana,sans-serif; + color: #074320; + cursor: pointer; + border-radius: 2px; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.c5 :hover { + border: 1px solid #074320; +} + +.c6 { + height: 24px; + width: 24px; + line-height: 24px; +} + +.c6 * { + height: inherit; + width: inherit; +} + +.c57 { + display: inline-block; + padding: 0 15px; + color: #4C4C4C; + text-align: center; + vertical-align: middle; + font-size: 11px; + font-weight: bold; + line-height: 30px; + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; + background-color: #fff; + border-radius: 2px; + border: 1px solid #bfbfbf; + cursor: pointer; + overflow: visible; + z-index: 1; +} + +.c57:focus, +.c57:hover { + border: 1px solid #4C4C4C; +} + +.c57:hover { + -webkit-text-decoration: none; + text-decoration: none; + background: #11ab51; + font-weight: bold; + color: #fff; +} + +.c57[disabled] { + cursor: not-allowed; + opacity: 0.65; + box-shadow: none; +} + +.c57 img { + height: 32px; + width: 32px; + margin-top: 5px 10px 5px -10px; + vertical-align: middle; +} + +.c57:link { + -webkit-text-decoration: none; + text-decoration: none; + color: #4C4C4C; +} + +.c58 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c59 { + border: 1px solid #7F7F7F; + color: #fff; + background: #11ab51; +} + +.c59 :hover { + color: #074320; + background: #A1DDBA; +} + +.c55 { + border-width: 1px 0 0 0; + border-style: solid; + border-color: #e5e5e5; + margin-top: 15px; + padding: 10px 20px 10px 20px; +} + +.c56 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c4 { + padding: 5px 5px 5px 10px; + margin-bottom: 15px; + border-radius: 2px 2px 0 0; + border-bottom: 1px solid #7F7F7F; + color: #fff; + font-weight: bold; + background: #11ab51; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + cursor: -webkit-grab; + cursor: grab; +} + +.c9 { + overflow: auto; + padding: 0 15px; + width: 100%; + height: 100%; + max-height: 400px; +} + +.c8 { + overflow: hidden; + height: 100%; +} + +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + padding-bottom: 10px; +} + +.c12 { + display: inline-block; + max-width: 100%; + font-weight: bold; + text-align: right; + padding-left: 10px; + padding-right: 10px; + width: 16.66666667%; + margin-left: 0; +} + +.c14 { + width: 83.33333333%; + padding-left: 10px; + padding-right: 10px; +} + +.c24 { + background-color: transparent; + border: none; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + outline: none; + margin: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; +} + +.c25 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c25 * { + height: inherit; + width: inherit; +} + +.c20 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; + background-color: #f3f3f3; +} + +.c46 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; +} + +.c19 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 180px; +} + +.c49 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 250px; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: default; +} + +.c47 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: pointer; +} + +.c17 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + +.c22 { + cursor: default; +} + +.c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c26 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; + width: 100%; +} + +.c27 a { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; +} + +.c27 a:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #000; +} + +.c28 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 25%; +} + +.c29 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 70%; +} + +.c30 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 10%; +} + +.c15 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; +} + +.c15:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c16 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c31 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c48 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c51 { + display: inline; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 0 3px; + margin-right: 4px; + margin-top: 1px; + margin-bottom: 0px; + background-color: #e5e5e5; + width: 80px; +} + +.c52 { + display: inline; + color: #7F7F7F; + margin-right: 2px; +} + +.c52:hover { + color: #000; +} + +.c53 { + text-overflow: ellipsis; + overflow: hidden; +} + +.c32 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + font-weight: normal; + cursor: pointer; +} + +.c35 { + font-family: inherit; + font-size: inherit; + padding: 0; + margin: 0; + margin-left: 10px; + line-height: normal; + width: auto; + height: auto; +} + +.c45 { + opacity: 1; +} + +.c44 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c36 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c39 { + border-radius: 2px; + border: 1px solid #bfbfbf; + background-color: #fff; + font-size: 1.1em; + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} + +.c40 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + border: none; + background: none; + color: inherit; + padding: 0; + margin: 0.2em 0; + vertical-align: middle; + margin-left: 0.4em; + margin-right: 22px; +} + +.c41 { + background-color: #e5e5e5; + color: #7F7F7F; + border-left: 1px solid #4C4C4C; + width: 16px; + height: 50%; + font-size: 0.6em; + padding: 0; + margin: 0; + text-align: center; + vertical-align: middle; + position: absolute; + right: 0; + cursor: default; + display: block; + overflow: hidden; + -webkit-text-decoration: none; + text-decoration: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.c41:hover { + background-color: #11ab51; + color: #fff; + -webkit-text-decoration: none; + text-decoration: none; +} + +.c41:active { + background-color: #fff; + color: #074320; + -webkit-text-decoration: none; + text-decoration: none; +} + +.c42 { + border-top-right-radius: 1px; + top: 0; +} + +.c43 { + border-bottom-right-radius: 1px; + bottom: 0; +} + +.c37 { + display: block; + height: auto; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 4px 8px; +} + +.c38 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +@media print { + .c26 { + border-collapse: collapse; + } +} + +@media print { + .c27 { + border-bottom: 1px solid black; + } + + .c27 a, + .c27 a:hover { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; + } +} + +@media print { + .c28 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c29 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c30 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + + +
+
+
+ +
+
+
+ +`; + +exports[`New Report Config Dialog component tests should render dialog 1`] = ` +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c13 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c23 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c26 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c1 { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin: 10% auto; + border: 0; + outline: 0; + width: 800px; + height: px; +} + +.c0 { + position: fixed; + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: 0; + background: rgba(102, 102, 102, 0.5); + z-index: 600; + -webkit-transition: opacity 1s ease-in; + transition: opacity 1s ease-in; + width: 100%; + height: 100%; +} + +.c33 { + width: 0; + height: 0; + cursor: nwse-resize; + border-right: 20px solid transparent; + border-bottom: 20px solid transparent; + border-top: 20px solid #fff; +} + +.c32 { + position: absolute; + bottom: 3px; + right: 3px; + width: 20px; + height: 20px; + background: repeating-linear-gradient( -45deg, transparent, transparent 2px, #000 2px, #000 3px ); +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: inherit; + padding: 0; + background: #fff; + box-shadow: 5px 5px 10px #7F7F7F; + border-radius: 3px; + border: 1px solid #7F7F7F; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-weight: bold; + font-size: 12px; + font-family: Verdana,sans-serif; + color: #074320; + cursor: pointer; + border-radius: 2px; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.c5 :hover { + border: 1px solid #074320; +} + +.c6 { + height: 24px; + width: 24px; + line-height: 24px; +} + +.c6 * { + height: inherit; + width: inherit; +} + +.c29 { + display: inline-block; + padding: 0 15px; + color: #4C4C4C; + text-align: center; + vertical-align: middle; + font-size: 11px; + font-weight: bold; + line-height: 30px; + -webkit-text-decoration: none; + text-decoration: none; + white-space: nowrap; + background-color: #fff; + border-radius: 2px; + border: 1px solid #bfbfbf; + cursor: pointer; + overflow: visible; + z-index: 1; +} + +.c29:focus, +.c29:hover { + border: 1px solid #4C4C4C; +} + +.c29:hover { + -webkit-text-decoration: none; + text-decoration: none; + background: #11ab51; + font-weight: bold; + color: #fff; +} + +.c29[disabled] { + cursor: not-allowed; + opacity: 0.65; + box-shadow: none; +} + +.c29 img { + height: 32px; + width: 32px; + margin-top: 5px 10px 5px -10px; + vertical-align: middle; +} + +.c29:link { + -webkit-text-decoration: none; + text-decoration: none; + color: #4C4C4C; +} + +.c30 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c31 { + border: 1px solid #7F7F7F; + color: #fff; + background: #11ab51; +} + +.c31 :hover { + color: #074320; + background: #A1DDBA; +} + +.c27 { + border-width: 1px 0 0 0; + border-style: solid; + border-color: #e5e5e5; + margin-top: 15px; + padding: 10px 20px 10px 20px; +} + +.c28 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c4 { + padding: 5px 5px 5px 10px; + margin-bottom: 15px; + border-radius: 2px 2px 0 0; + border-bottom: 1px solid #7F7F7F; + color: #fff; + font-weight: bold; + background: #11ab51; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + cursor: -webkit-grab; + cursor: grab; +} + +.c9 { + overflow: auto; + padding: 0 15px; + width: 100%; + height: 100%; + max-height: 400px; +} + +.c8 { + overflow: hidden; + height: 100%; +} + +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + padding-bottom: 10px; +} + +.c12 { + display: inline-block; + max-width: 100%; + font-weight: bold; + text-align: right; + padding-left: 10px; + padding-right: 10px; + width: 16.66666667%; + margin-left: 0; +} + +.c14 { + width: 83.33333333%; + padding-left: 10px; + padding-right: 10px; +} + +.c24 { + background-color: transparent; + border: none; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + outline: none; + margin: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; +} + +.c25 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c25 * { + height: inherit; + width: inherit; +} + +.c20 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; +} + +.c19 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 180px; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: pointer; +} + +.c17 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + +.c22 { + cursor: default; +} + +.c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c15 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; +} + +.c15:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c16 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + + +
+
+
+ +
+
+ +`; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/listpage.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/listpage.js.snap new file mode 100644 index 0000000000..1ba32a3e83 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/listpage.js.snap @@ -0,0 +1,1519 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ReportConfigsPage tests ReportConfigsPage ToolBarIcons test should render 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c2 { + margin-left: -5px; +} + +.c2>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c2>* { + margin-left: 5px; +} + +.c1 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c4 { + cursor: pointer; +} + +.c3 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c3 * { + height: inherit; + width: inherit; +} + +@media print { + .c4 { + display: none; + } +} + +
+
+ + + + help.svg + + + + + + new.svg + + +
+
+`; + +exports[`ReportConfigsPage tests should render full ReportConfigsPage 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c1 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: stetch; + -webkit-box-align: stetch; + -ms-flex-align: stetch; + align-items: stetch; +} + +.c9 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c10 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c24 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c28 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c33 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c35 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c47 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; +} + +.c48 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c49 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c52 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c53 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c51 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c4 { + margin-left: -5px; +} + +.c4>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c4>* { + margin-left: 5px; +} + +.c3 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c6 { + cursor: pointer; +} + +.c37 svg path { + fill: #bfbfbf; +} + +.c5 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c5 * { + height: inherit; + width: inherit; +} + +.c30 { + height: 50px; + width: 50px; + line-height: 50px; +} + +.c30 * { + height: inherit; + width: inherit; +} + +.c25 { + margin: 10px 0px; + padding-bottom: 1px; + border-bottom: 2px solid #e5e5e5; + position: relative; +} + +.c26 { + margin: 0 0 1px 0; +} + +.c27 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c29 { + margin-right: 5px; +} + +.c31 { + word-break: break-all; + min-width: 100px; +} + +.c22 { + background-color: transparent; + border: none; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + outline: none; + margin: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; +} + +.c23 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c23 * { + height: inherit; + width: inherit; +} + +.c18 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; +} + +.c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 150px; +} + +.c54 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 180px; +} + +.c19 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: pointer; +} + +.c15 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + +.c20 { + cursor: default; +} + +.c16 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c39 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; + width: 100%; +} + +.c40 th, +.c40 td { + padding: 4px 10px; + border-bottom: 1px solid #e5e5e5; +} + +.c40 tfoot tr { + background: #fff; +} + +.c40 tfoot tr td { + border-bottom: 1px solid #e5e5e5; +} + +.c42 a { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; +} + +.c42 a:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #000; +} + +.c44 { + cursor: pointer; +} + +.c43 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 25%; +} + +.c45 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 50%; +} + +.c46 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 8%; +} + +.c13 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; +} + +.c13:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c14 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c12 { + margin-right: 5px; +} + +.c11 { + margin-right: 5px; +} + +.c55 { + font-size: 10px; + color: #7F7F7F; + text-align: left; +} + +.c38 { + margin: 0 3px; +} + +.c36 { + margin: 2px 3px; +} + +.c41 { + opacity: 1.0; +} + +.c34 { + margin-top: 2px; + margin-left: 2px; +} + +.c32 { + margin-top: 20px; +} + +.c50 { + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + color: #0a53b8; +} + +.c50 :hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #0a53b8; +} + +@media print { + .c6 { + display: none; + } +} + +@media print { + .c39 { + border-collapse: collapse; + } +} + +@media screen { + .c40>tbody:nth-of-type(even), + .c40>tbody:only-of-type>tr:nth-of-type(even) { + background: #f3f3f3; + } + + .c40>tbody:not(:only-of-type):hover, + .c40>tbody:only-of-type>tr:hover { + background: #e5e5e5; + } +} + +@media print { + .c42 { + border-bottom: 1px solid black; + } + + .c42 a, + .c42 a:hover { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; + } +} + +@media print { + .c43 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c45 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c46 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c36 svg { + display: none; + } +} + +@media print { + .c50 { + color: #000; + } +} + + +
+
+
+
+
+
+ + + + help.svg + + + + + + new.svg + + +
+
+
+
+
+
+
+
+ + +
+ × +
+
+
+
+ + + refresh.svg + + + + + delete.svg + + + + + reset.svg + + + + + + help.svg + + + + + + edit.svg + + +
+
+
+
+ +
+
+
+
+
+
+

+
+ + + report_format.svg + + +
+
+ Report Configs 0 of 0 +
+

+
+
+
+
+
+ + + fold.svg + + +
+
+
+ + + first.svg + + + + + previous.svg + + +
+
+ + 0 - 0 of 0 + +
+
+ + + next.svg + + + + + last.svg + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ Name +
+
+
+ +
+ Report Format +
+
+
+
+ Actions +
+
+
+
+
+ + + foo + + +
+
+
+ ( + bar + ) +
+
+
+
+ + + baz + + +
+
+
+
+
+ + + trashcan.svg + + + + + edit.svg + + + + + clone.svg + + + + + export.svg + + +
+
+
+
+
+
+
+ +
+
+ + + tags.svg + + + + + trashcan.svg + + +
+
+
+
+
+
+
+
+ (Applied filter: ) +
+
+
+
+ + + first.svg + + + + + previous.svg + + +
+
+ + 0 - 0 of 0 + +
+
+ + + next.svg + + + + + last.svg + + +
+
+
+
+
+
+
+
+ +
+ +`; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/row.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/row.js.snap new file mode 100644 index 0000000000..77a171d1ee --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/row.js.snap @@ -0,0 +1,575 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Report Config row tests should render 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c1 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c6 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c4 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c9 { + margin-left: -5px; +} + +.c9>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c9>* { + margin-left: 5px; +} + +.c7 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c10 { + cursor: pointer; +} + +.c3 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c3 * { + height: inherit; + width: inherit; +} + +.c2 { + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + color: #0a53b8; +} + +.c2 :hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #0a53b8; +} + +@media print { + .c10 { + display: none; + } +} + +@media print { + .c2 { + color: #000; + } +} + + +
+
+ + +
+
+
+ + + foo + + +
+ + + view_other.svg + + +
+
+ ( + bar + ) +
+
+ + +
+ + + baz + + +
+ + +
+
+
+ + + trashcan.svg + + + + + edit.svg + + + + + clone.svg + + + + + export.svg + + +
+
+
+ + +
+ +`; + +exports[`Report Config row tests should render orphan 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c1 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c8 { + margin-left: -5px; +} + +.c8>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c8>* { + margin-left: 5px; +} + +.c6 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c9 { + cursor: pointer; +} + +.c3 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c3 * { + height: inherit; + width: inherit; +} + +.c2 { + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + color: #0a53b8; +} + +.c2 :hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #0a53b8; +} + +@media print { + .c9 { + display: none; + } +} + +@media print { + .c2 { + color: #000; + } +} + + +
+
+ + +
+
+
+ + Orphan + + + + foo + + +
+ + + view_other.svg + + +
+
+ ( + bar + ) +
+
+ + +
+ + 4321 + +
+ + +
+
+
+ + + trashcan.svg + + + + + edit.svg + + + + + clone.svg + + + + + export.svg + + +
+
+
+ + +
+ +`; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/table.js.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/table.js.snap new file mode 100644 index 0000000000..eb77f1d94c --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/table.js.snap @@ -0,0 +1,1132 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Scan Config table tests should render 1`] = ` +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c2 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c6 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c8 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c20 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; +} + +.c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c22 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; +} + +.c25 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c26 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c32 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c24 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c10 { + margin-left: -5px; +} + +.c10>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c10>* { + margin-left: 5px; +} + +.c9 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c3 { + cursor: pointer; +} + +.c11 svg path { + fill: #bfbfbf; +} + +.c4 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c4 * { + height: inherit; + width: inherit; +} + +.c33 { + background-color: transparent; + border: none; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + outline: none; + margin: 1px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; +} + +.c34 { + height: 16px; + width: 16px; + line-height: 16px; +} + +.c34 * { + height: inherit; + width: inherit; +} + +.c29 { + border: 1px solid #bfbfbf; + border-radius: 2px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 1px 5px; + background-color: #fff; + color: #000; + font-weight: normal; +} + +.c28 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + position: relative; + width: 180px; +} + +.c30 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + cursor: pointer; +} + +.c35 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + +.c31 { + cursor: default; +} + +.c27 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c13 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; + width: 100%; +} + +.c14 th, +.c14 td { + padding: 4px 10px; + border-bottom: 1px solid #e5e5e5; +} + +.c14 tfoot tr { + background: #fff; +} + +.c14 tfoot tr td { + border-bottom: 1px solid #e5e5e5; +} + +.c16 a { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; +} + +.c16 a:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #000; +} + +.c17 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 25%; +} + +.c18 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 50%; +} + +.c19 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 8%; +} + +.c36 { + font-size: 10px; + color: #7F7F7F; + text-align: left; +} + +.c12 { + margin: 0 3px; +} + +.c7 { + margin: 2px 3px; +} + +.c15 { + opacity: 1.0; +} + +.c5 { + margin-top: 2px; + margin-left: 2px; +} + +.c1 { + margin-top: 20px; +} + +.c23 { + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + color: #0a53b8; +} + +.c23 :hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #0a53b8; +} + +@media print { + .c3 { + display: none; + } +} + +@media print { + .c13 { + border-collapse: collapse; + } +} + +@media screen { + .c14>tbody:nth-of-type(even), + .c14>tbody:only-of-type>tr:nth-of-type(even) { + background: #f3f3f3; + } + + .c14>tbody:not(:only-of-type):hover, + .c14>tbody:only-of-type>tr:hover { + background: #e5e5e5; + } +} + +@media print { + .c16 { + border-bottom: 1px solid black; + } + + .c16 a, + .c16 a:hover { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; + } +} + +@media print { + .c17 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c18 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c19 { + color: #000; + font-size: 1.2em; + background-color: transparent; + font-weight: bold; + } +} + +@media print { + .c7 svg { + display: none; + } +} + +@media print { + .c23 { + color: #000; + } +} + + +
+
+
+
+ + + fold.svg + + +
+
+
+ + + first.svg + + + + + previous.svg + + +
+
+ + 1 - 1 of 1 + +
+
+ + + next.svg + + + + + last.svg + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Name +
+
+
+ Report Format +
+
+
+ Actions +
+
+
+
+
+ + + foo + + +
+
+
+ ( + bar + ) +
+
+
+
+ + + baz + + +
+
+
+
+
+ + + trashcan.svg + + + + + edit.svg + + + + + clone.svg + + + + + export.svg + + +
+
+
+
+
+
+
+ + + lorem + + +
+
+
+ ( + ipsum + ) +
+
+
+
+ + + dolor + + +
+
+
+
+
+ + + trashcan.svg + + + + + edit.svg + + + + + clone.svg + + + + + export.svg + + +
+
+
+
+
+
+
+ + + hello + + +
+
+
+ ( + world + ) +
+
+
+
+ + + baz + + +
+
+
+
+
+ + + trashcan.svg + + + + + edit.svg + + + + + clone.svg + + + + + export.svg + + +
+
+
+
+
+
+
+ +
+
+
+
+
+ (Applied filter: rows=2) +
+
+
+
+ + + first.svg + + + + + previous.svg + + +
+
+ + 1 - 1 of 1 + +
+
+ + + next.svg + + + + + last.svg + + +
+
+
+
+
+
+ +`; diff --git a/src/web/pages/reportconfigs/__tests__/component.js b/src/web/pages/reportconfigs/__tests__/component.js new file mode 100644 index 0000000000..e1c82d829e --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/component.js @@ -0,0 +1,275 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import ReportFormatComponent from '../component'; +import {rendererWith, wait} from 'web/utils/testing'; +import ReportConfig from 'gmp/models/reportconfig'; +import { + fireEvent, + getAllByRole, + getAllByTestId, + getByTestId, +} from '@testing-library/react'; +import Capabilities from 'gmp/capabilities/capabilities'; + +describe('Report Config Component tests', () => { + const mockReportConfig = ReportConfig.fromElement({ + _id: 'rc123', + name: 'test report config', + report_format: { + _id: 'rf456', + name: 'test report format', + }, + param: [ + { + name: 'test param', + value: 'ABC', + type: { + __text: 'string', + min: '0', + max: '1', + }, + }, + ], + }); + + const mockReportFormat = { + id: 'rf456', + name: 'test report format', + configurable: true, + params: [ + { + name: 'test param', + value: 'ABC', + type: 'string', + }, + ], + }; + + test('should open edit dialog and call GMP save', async () => { + let editClick; + const children = jest.fn(({edit}) => { + editClick = edit; + }); + + const handleInteraction = jest.fn(); + const getReportConfig = jest.fn().mockResolvedValue({ + data: mockReportConfig, + }); + const getAllReportFormats = jest.fn().mockResolvedValue({ + data: [mockReportFormat], + }); + const saveReportConfig = jest.fn().mockResolvedValue({ + data: {}, + }); + + const gmp = { + user: { + currentSettings: jest.fn().mockResolvedValue({ + data: {}, + }), + }, + reportconfig: { + get: getReportConfig, + save: saveReportConfig, + }, + reportformats: { + getAll: getAllReportFormats, + }, + }; + + const {render} = rendererWith({ + gmp, + router: true, + store: true, + }); + + const {baseElement} = render( + + {children} + , + ); + editClick({id: 'rc123'}); + + await wait(); + expect(baseElement).toMatchSnapshot(); + + expect(getReportConfig).toHaveBeenCalledWith({ + id: 'rc123', + }); + expect(getAllReportFormats).toHaveBeenCalledWith(); + + const titleBar = getByTestId(baseElement, 'dialog-title-bar'); + expect(titleBar).toHaveTextContent('Edit Report Config test report config'); + const content = getByTestId(baseElement, 'save-dialog-content'); + expect(content).toHaveTextContent('test report format'); + expect(content).toHaveTextContent('test param'); + + const saveButton = getByTestId(baseElement, 'dialog-save-button'); + fireEvent.click(saveButton); + + expect(saveReportConfig).toHaveBeenCalledWith({ + alerts: [], + comment: '', + entityType: 'reportconfig', + id: 'rc123', + name: 'test report config', + params: { + 'test param': 'ABC', + }, + params_using_default: { + 'test param': false, + }, + report_format: 'rf456', + report_format_id: undefined, + userCapabilities: new Capabilities(), + userTags: [], + }); + }); + + test('should open create dialog and call GMP create', async () => { + let createClick; + const children = jest.fn(({edit, create}) => { + createClick = create; + }); + const handleInteraction = jest.fn(); + const getAllReportFormats = jest.fn().mockResolvedValue({ + data: [mockReportFormat], + }); + const getReportFormat = jest.fn().mockResolvedValue({ + data: mockReportFormat, + }); + const createReportConfig = jest.fn().mockResolvedValue({ + data: {}, + }); + + const gmp = { + user: { + currentSettings: jest.fn().mockResolvedValue({ + data: {}, + }), + }, + reportconfig: { + create: createReportConfig, + }, + reportformat: { + get: getReportFormat, + }, + reportformats: { + getAll: getAllReportFormats, + }, + }; + + const {render} = rendererWith({ + gmp, + router: true, + store: true, + }); + + const {baseElement} = render( + + {children} + , + ); + createClick(); + + await wait(); + expect(baseElement).toMatchSnapshot(); + + expect(getAllReportFormats).toHaveBeenCalledWith(); + + const titleBar = getByTestId(baseElement, 'dialog-title-bar'); + expect(titleBar).toHaveTextContent('New Report Config'); + const content = getByTestId(baseElement, 'save-dialog-content'); + + // No report format selected at start + expect(content).not.toHaveTextContent('test report format'); + // No params before report format has been selected + expect(content).not.toHaveTextContent('test param'); + + // Choose report format + const comboBoxes = getAllByRole(content, 'combobox'); + fireEvent.click(getByTestId(comboBoxes[0], 'select-open-button')); + const menuId = comboBoxes[0].getAttribute('aria-owns'); + const menuItems = getAllByTestId( + baseElement.querySelector('#' + menuId), + 'select-item', + ); + fireEvent.click(menuItems[0]); + await wait(); + + const saveButton = getByTestId(baseElement, 'dialog-save-button'); + fireEvent.click(saveButton); + + expect(createReportConfig).toHaveBeenCalledWith({ + name: 'Unnamed', + comment: '', + params: { + 'test param': 'ABC', + }, + params_using_default: { + 'test param': true, + }, + report_format_id: 'rf456', + }); + }); + + test('should open and close create dialog', async () => { + let createClick; + const children = jest.fn(({edit, create}) => { + createClick = create; + }); + const handleInteraction = jest.fn(); + const getAllReportFormats = jest.fn().mockResolvedValue({ + data: [mockReportFormat], + }); + + const gmp = { + user: { + currentSettings: jest.fn().mockResolvedValue({ + data: {}, + }), + }, + reportformats: { + getAll: getAllReportFormats, + }, + }; + + const {render} = rendererWith({ + gmp, + router: true, + store: true, + }); + + const {baseElement} = render( + + {children} + , + ); + createClick(); + await wait(); + + expect(baseElement).toHaveTextContent('New Report Config'); + + const closeButton = getByTestId(baseElement, 'dialog-close-button'); + fireEvent.click(closeButton); + await wait(); + + expect(baseElement).not.toHaveTextContent('New Report Config'); + }); +}); diff --git a/src/web/pages/reportconfigs/__tests__/details.js b/src/web/pages/reportconfigs/__tests__/details.js new file mode 100644 index 0000000000..b4ed5f4fd1 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/details.js @@ -0,0 +1,95 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import React from 'react'; + +import Capabilities from 'gmp/capabilities/capabilities'; +import ReportConfig from 'gmp/models/reportconfig'; + +import {rendererWith} from 'web/utils/testing'; + +import Details from '../details'; +import {mockReportConfig} from '../__mocks__/mockreportconfig'; + +describe('Report Config Details tests', () => { + test('should render full Details', () => { + const config = ReportConfig.fromElement(mockReportConfig); + + const caps = new Capabilities(['everything']); + + const {render} = rendererWith({capabilities: caps, router: true}); + + const {element, getAllByTestId} = render(
); + + expect(element).toMatchSnapshot(); + + expect(element).toHaveTextContent('StringParam'); + expect(element).toHaveTextContent('StringValue'); + + expect(element).toHaveTextContent('TextParam'); + const preElements = element.querySelectorAll('pre'); + expect(preElements[0]).toHaveTextContent('TextValue'); + + expect(element).toHaveTextContent('IntegerParam'); + expect(element).toHaveTextContent('12'); + expect(element).toHaveTextContent('SelectionParam'); + expect(element).toHaveTextContent('OptionB'); + expect(element).toHaveTextContent('ReportFormatListParam'); + expect(element).toHaveTextContent('non-configurable'); + + const detailslinks = getAllByTestId('details-link'); + + // Report format of the config + expect(detailslinks[0]).toHaveTextContent('example-configurable-1'); + expect(detailslinks[0]).toHaveAttribute('href', '/reportformat/123456'); + + // Report format params + expect(detailslinks[1]).toHaveTextContent('non-configurable-1'); + expect(detailslinks[1]).toHaveAttribute('href', '/reportformat/654321'); + + expect(detailslinks[2]).toHaveTextContent('non-configurable-2'); + expect(detailslinks[2]).toHaveAttribute('href', '/reportformat/7654321'); + + // Alerts + expect(detailslinks[3]).toHaveTextContent('ABC'); + expect(detailslinks[3]).toHaveAttribute('href', '/alert/321'); + + expect(detailslinks[4]).toHaveTextContent('XYZ'); + expect(detailslinks[4]).toHaveAttribute('href', '/alert/789'); + }); + + test('should render orphaned config details', () => { + const config = ReportConfig.fromElement({ + _id: '123', + name: 'foo', + comment: 'bar', + orphan: '1', + }); + + const caps = new Capabilities(['everything']); + + const {render} = rendererWith({capabilities: caps, router: true}); + + const {element} = render(
); + + expect(element).toMatchSnapshot(); + + expect(element).toHaveTextContent( + 'not available for orphaned report configs', + ); + }); +}); diff --git a/src/web/pages/reportconfigs/__tests__/detailspage.js b/src/web/pages/reportconfigs/__tests__/detailspage.js new file mode 100644 index 0000000000..aa691f99fd --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/detailspage.js @@ -0,0 +1,173 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import React from 'react'; + +import Capabilities from 'gmp/capabilities/capabilities'; +import ReportConfig from 'gmp/models/reportconfig'; + +import {entityLoadingActions} from 'web/store/entities/reportconfigs'; + +import {rendererWith, fireEvent} from 'web/utils/testing'; + +import DetailsPage from '../detailspage'; +import Filter from 'gmp/models/filter'; +import CollectionCounts from 'gmp/collection/collectioncounts'; +import {setTimezone, setUsername} from 'web/store/usersettings/actions'; +import {setLocale} from 'gmp/locale/date'; + +import {mockReportConfig} from '../__mocks__/mockreportconfig'; + +setLocale('en'); + +const entityType = 'reportconfig'; +const reloadInterval = 1; +const manualUrl = 'test/'; + +const currentSettings = jest.fn().mockResolvedValue({ + foo: 'bar', +}); + +const renewSession = jest.fn().mockResolvedValue({ + foo: 'bar', +}); + +const getPermissions = jest.fn().mockResolvedValue({ + data: [], + meta: { + filter: Filter.fromString(), + counts: new CollectionCounts(), + }, +}); + +const config = ReportConfig.fromElement(mockReportConfig); +describe('Report Config Details Page tests', () => { + test('should render full Details page with param details', () => { + const getReportConfig = jest.fn().mockResolvedValue({ + data: config, + }); + + const gmp = { + [entityType]: { + get: getReportConfig, + }, + permissions: { + get: getPermissions, + }, + reloadInterval, + settings: {manualUrl}, + user: { + currentSettings, + renewSession, + }, + }; + + const caps = new Capabilities(['everything']); + + const {render, store} = rendererWith({ + capabilities: caps, + gmp, + router: true, + store: true, + }); + + store.dispatch(setTimezone('CET')); + store.dispatch(setUsername('admin')); + + store.dispatch(entityLoadingActions.success('12345', config)); + + const {element} = render(); + expect(element).toMatchSnapshot(); + + // Test parameter details + const spans = element.querySelectorAll('span'); + expect(spans[8]).toHaveTextContent('Parameter Details'); + fireEvent.click(spans[8]); + + const paramTableRows = element.querySelectorAll('tr'); + expect(paramTableRows.length).toBe(7); + + let columns = paramTableRows[0].querySelectorAll('th'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('Name'); + expect(columns[1]).toHaveTextContent('Value'); + expect(columns[2]).toHaveTextContent('Using Default'); + expect(columns[3]).toHaveTextContent('Default Value'); + expect(columns[4]).toHaveTextContent('Minimum'); + expect(columns[5]).toHaveTextContent('Maximum'); + + columns = paramTableRows[1].querySelectorAll('td'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('StringParam'); + expect(columns[1]).toHaveTextContent('StringValue'); + expect(columns[2]).toHaveTextContent('Yes'); + expect(columns[3]).toHaveTextContent('StringValue'); + expect(columns[4]).toHaveTextContent('0'); + expect(columns[5]).toHaveTextContent('1'); + + columns = paramTableRows[2].querySelectorAll('td'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('TextParam'); + expect(columns[1]).toHaveTextContent('TextValue'); + expect(columns[2]).toHaveTextContent('No'); + expect(columns[3]).toHaveTextContent('TextDefault'); + expect(columns[4]).toHaveTextContent('0'); + expect(columns[5]).toHaveTextContent('1'); + + columns = paramTableRows[3].querySelectorAll('td'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('IntegerParam'); + expect(columns[1]).toHaveTextContent('12'); + expect(columns[2]).toHaveTextContent('Yes'); + expect(columns[3]).toHaveTextContent('12'); + expect(columns[4]).toHaveTextContent('0'); + expect(columns[5]).toHaveTextContent('50'); + + columns = paramTableRows[4].querySelectorAll('td'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('BooleanParam'); + expect(columns[1]).toHaveTextContent('Yes'); + expect(columns[2]).toHaveTextContent('No'); + expect(columns[3]).toHaveTextContent('No'); + expect(columns[4]).toHaveTextContent('0'); + expect(columns[5]).toHaveTextContent('1'); + + columns = paramTableRows[5].querySelectorAll('td'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('SelectionParam'); + expect(columns[1]).toHaveTextContent('OptionB'); + expect(columns[2]).toHaveTextContent('No'); + expect(columns[3]).toHaveTextContent('OptionA'); + + columns = paramTableRows[6].querySelectorAll('td'); + expect(columns.length).toBe(6); + expect(columns[0]).toHaveTextContent('ReportFormatListParam'); + let detailsLinks = columns[1].querySelectorAll('a'); + expect(detailsLinks).toHaveLength(2); + expect(detailsLinks[0]).toHaveTextContent('non-configurable-1'); + expect(detailsLinks[0]).toHaveAttribute('href', '/reportformat/654321'); + expect(detailsLinks[1]).toHaveTextContent('non-configurable-2'); + expect(detailsLinks[1]).toHaveAttribute('href', '/reportformat/7654321'); + expect(columns[2]).toHaveTextContent('No'); + detailsLinks = columns[3].querySelectorAll('a'); + expect(detailsLinks).toHaveLength(2); + expect(detailsLinks[0]).toHaveTextContent('non-configurable-2'); + expect(detailsLinks[0]).toHaveAttribute('href', '/reportformat/7654321'); + expect(detailsLinks[1]).toHaveTextContent('configurable-2'); + expect(detailsLinks[1]).toHaveAttribute('href', '/reportformat/1234567'); + }); +}); diff --git a/src/web/pages/reportconfigs/__tests__/dialog.js b/src/web/pages/reportconfigs/__tests__/dialog.js new file mode 100644 index 0000000000..65880d2edd --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/dialog.js @@ -0,0 +1,445 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import React from 'react'; + +import {setLocale} from 'gmp/locale/lang'; +import ReportConfig from 'gmp/models/reportconfig'; + +import {rendererWith, fireEvent, getAllByTestId, wait} from 'web/utils/testing'; + +import ReportConfigDialog from '../dialog'; + +import {mockReportConfig} from 'web/pages/reportconfigs/__mocks__/mockreportconfig'; +import {mockReportFormats} from '../__mocks__/mockreportformats'; +import {getAllByRole, getByRole, getByTestId} from '@testing-library/react'; +import ReportFormat from 'gmp/models/reportformat'; + +setLocale('en'); + +const config = ReportConfig.fromElement(mockReportConfig); +describe('Edit Report Config Dialog component tests', () => { + test('should render dialog with disabled report format selection', () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + + const gmp = {}; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + + const {baseElement} = render( + , + ); + + expect(baseElement).toMatchSnapshot(); + + const titleBar = getByTestId(baseElement, 'dialog-title-bar'); + expect(titleBar).toHaveTextContent('Edit Report Config'); + + const content = getByTestId(baseElement, 'save-dialog-content'); + + const comboBoxes = getAllByRole(content, 'combobox'); + expect(comboBoxes[0]).toHaveTextContent('example-configurable-1'); + expect(getByTestId(comboBoxes[0], 'select-selected-value')).toHaveAttribute( + 'disabled', + ); + }); + + test('should save data', () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + + const gmp = {}; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + const {baseElement} = render( + , + ); + + const saveButton = getByTestId(baseElement, 'dialog-save-button'); + fireEvent.click(saveButton); + + expect(handleSave).toHaveBeenCalledWith({ + ...config, + params: { + BooleanParam: true, + IntegerParam: 12, + ReportFormatListParam: ['654321', '7654321'], + SelectionParam: 'OptionB', + StringParam: 'StringValue', + TextParam: 'TextValue', + }, + params_using_default: { + BooleanParam: false, + IntegerParam: true, + ReportFormatListParam: false, + SelectionParam: false, + StringParam: true, + TextParam: false, + }, + report_format: config.report_format.id, + report_format_id: undefined, + }); + }); + + test('should allow to close the dialog', () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + + const gmp = {}; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + const {baseElement} = render( + , + ); + + const closeButton = getByTestId(baseElement, 'dialog-close-button'); + + fireEvent.click(closeButton); + + expect(handleClose).toHaveBeenCalled(); + expect(handleSave).not.toHaveBeenCalled(); + }); + + test('should allow to change name, comment and params', () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + const handleValueChange = jest.fn(); + + const gmp = {}; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + const {baseElement} = render( + , + ); + + const content = getByTestId(baseElement, 'save-dialog-content'); + const comboBoxes = getAllByRole(content, 'combobox'); + let inputs; + + // Set name and comment + inputs = content.querySelectorAll('input[name="name"]'); + fireEvent.change(inputs[0], {target: {value: 'lorem'}}); + + inputs = content.querySelectorAll('input[name="comment"]'); + fireEvent.change(inputs[0], {target: {value: 'ipsum'}}); + + // Set params + inputs = content.querySelectorAll('input[name="BooleanParam"]'); + fireEvent.click(inputs[1]); + + inputs = content.querySelectorAll('input[name="IntegerParam"]'); + fireEvent.change(inputs[0], {target: {value: '7'}}); + + inputs = content.querySelectorAll('input[name="StringParam"]'); + fireEvent.change(inputs[0], {target: {value: 'NewString'}}); + + inputs = content.querySelectorAll('textarea[name="TextParam"]'); + fireEvent.change(inputs[0], {target: {value: 'NewText'}}); + + // Choose new SelectionParam + fireEvent.click(getByTestId(comboBoxes[1], 'select-open-button')); + const menuId = comboBoxes[1].getAttribute('aria-owns'); + const menuItems = getAllByTestId( + baseElement.querySelector('#' + menuId), + 'select-item', + ); + fireEvent.click(menuItems[0]); + + // Unselect report format from ReportFormatListParam + const multiselectDeleteButtons = getAllByTestId( + comboBoxes[2], + 'multiselect-selected-delete', + ); + fireEvent.click(multiselectDeleteButtons[1]); + + expect(handleValueChange).toHaveBeenCalledTimes(6); + + const saveButton = getByTestId(baseElement, 'dialog-save-button'); + fireEvent.click(saveButton); + + expect(handleSave).toHaveBeenCalledWith({ + ...config, + name: 'lorem', + comment: 'ipsum', + params: { + BooleanParam: false, + IntegerParam: 7, + StringParam: 'NewString', + ReportFormatListParam: ['654321'], + SelectionParam: 'OptionA', + TextParam: 'NewText', + }, + params_using_default: { + BooleanParam: false, + IntegerParam: false, + StringParam: false, + ReportFormatListParam: false, + SelectionParam: false, + TextParam: false, + }, + report_format: config.report_format.id, + report_format_id: undefined, + }); + }); + + test('should be able to toggle which params use default value', () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + const handleValueChange = jest.fn(); + + const gmp = {}; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + const {baseElement} = render( + , + ); + + const content = getByTestId(baseElement, 'save-dialog-content'); + let inputs; + + inputs = content.querySelectorAll('input[name="BooleanParam"]'); + fireEvent.click(inputs[2]); + + inputs = content.querySelectorAll('input[name="IntegerParam"]'); + fireEvent.click(inputs[1]); + + inputs = content.querySelectorAll('input[name="StringParam"]'); + fireEvent.click(inputs[1]); + + inputs = content.querySelectorAll('input[name="TextParam"]'); + fireEvent.click(inputs[0]); + + inputs = content.querySelectorAll('input[name="ReportFormatListParam"]'); + fireEvent.click(inputs[0]); + + inputs = content.querySelectorAll('input[name="SelectionParam"]'); + fireEvent.click(inputs[0]); + + expect(handleValueChange).toHaveBeenCalledTimes(6); + + const saveButton = getByTestId(baseElement, 'dialog-save-button'); + fireEvent.click(saveButton); + + expect(handleSave).toHaveBeenCalledWith({ + ...config, + params: { + BooleanParam: true, + IntegerParam: 12, + ReportFormatListParam: ['654321', '7654321'], + SelectionParam: 'OptionB', + StringParam: 'StringValue', + TextParam: 'TextValue', + }, + // Should be reverse of "shpuld save data" case + params_using_default: { + BooleanParam: true, + IntegerParam: false, + ReportFormatListParam: true, + SelectionParam: true, + StringParam: false, + TextParam: true, + }, + report_format: config.report_format.id, + report_format_id: undefined, + }); + }); +}); + +describe('New Report Config Dialog component tests', () => { + test('should render dialog', () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + + const gmp = {}; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + + const {baseElement} = render( + , + ); + + expect(baseElement).toMatchSnapshot(); + + const titleBar = getByTestId(baseElement, 'dialog-title-bar'); + expect(titleBar).toHaveTextContent('New Report Config'); + + expect(baseElement).not.toHaveTextContent('Param'); + }); + + test('should allow to change name, comment, report_format and params', async () => { + const handleClose = jest.fn(); + const handleSave = jest.fn(); + const mockReportFormatDetails = ReportFormat.fromElement({ + _id: '1234567', + name: 'example-configurable-2', + configurable: '1', + param: [ + { + name: 'Param1', + value: 'ABC', + type: { + __text: 'string', + min: 0, + max: 100, + }, + }, + { + name: 'Param2', + value: 'DEF', + type: { + __text: 'string', + min: 0, + max: 100, + }, + }, + { + name: 'ReportFormatListParam', + value: 'DEF', + type: { + __text: 'report_format_list', + min: 0, + max: 100, + }, + }, + ], + }); + + const getReportFormat = jest.fn().mockResolvedValue({ + data: mockReportFormatDetails, + }); + + const gmp = { + reportformat: { + get: getReportFormat, + }, + }; + const formats = mockReportFormats; + + const {render} = rendererWith({capabilities: true, router: true, gmp}); + + const handleValueChange = jest.fn(); + + const {baseElement} = render( + , + ); + + const content = getByTestId(baseElement, 'save-dialog-content'); + let comboBoxes = getAllByRole(content, 'combobox'); + let inputs; + let menuId; + let menuItems; + + inputs = content.querySelectorAll('input[name="name"]'); + fireEvent.change(inputs[0], {target: {value: 'lorem'}}); + + inputs = content.querySelectorAll('input[name="comment"]'); + fireEvent.change(inputs[0], {target: {value: 'ipsum'}}); + + // Choose new report format + fireEvent.click(getByTestId(comboBoxes[0], 'select-open-button')); + menuId = comboBoxes[0].getAttribute('aria-owns'); + menuItems = getAllByTestId( + baseElement.querySelector('#' + menuId), + 'select-item', + ); + fireEvent.click(menuItems[1]); + await wait(); + + // Set params + expect(getReportFormat).toHaveBeenCalledWith({id: '1234567'}); + inputs = content.querySelectorAll('input[name="Param2"]'); + fireEvent.change(inputs[0], {target: {value: 'XYZ'}}); + + comboBoxes = getAllByRole(content, 'combobox'); + fireEvent.click(getByRole(comboBoxes[1], 'button')); + menuId = comboBoxes[1].getAttribute('aria-owns'); + menuItems = getAllByTestId( + baseElement.querySelector('#' + menuId), + 'multiselect-item-label', + ); + fireEvent.click(menuItems[1]); + + const saveButton = getByTestId(baseElement, 'dialog-save-button'); + fireEvent.click(saveButton); + + expect(handleSave).toHaveBeenCalledWith({ + name: 'lorem', + comment: 'ipsum', + report_format_id: '1234567', + params: { + Param1: 'ABC', + Param2: 'XYZ', + ReportFormatListParam: ['654321'], + }, + params_using_default: { + Param1: true, + Param2: false, + ReportFormatListParam: false, + }, + }); + }); +}); diff --git a/src/web/pages/reportconfigs/__tests__/listpage.js b/src/web/pages/reportconfigs/__tests__/listpage.js new file mode 100644 index 0000000000..9cf9500e66 --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/listpage.js @@ -0,0 +1,281 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import React from 'react'; +import {act} from 'react-dom/test-utils'; + +import Capabilities from 'gmp/capabilities/capabilities'; +import CollectionCounts from 'gmp/collection/collectioncounts'; + +import Filter from 'gmp/models/filter'; +import ReportConfig from 'gmp/models/reportconfig'; + +import {setUsername} from 'web/store/usersettings/actions'; + +import {entitiesLoadingActions} from 'web/store/entities/scanconfigs'; +import {loadingActions} from 'web/store/usersettings/defaults/actions'; +import {defaultFilterLoadingActions} from 'web/store/usersettings/defaultfilters/actions'; + +import {rendererWith, waitFor, fireEvent} from 'web/utils/testing'; + +import ReportConfigsPage, {ToolBarIcons} from '../listpage'; + +window.URL.createObjectURL = jest.fn(); + +const config = ReportConfig.fromElement({ + _id: '12345', + name: 'foo', + comment: 'bar', + creation_time: '2019-07-16T06:31:29Z', + modification_time: '2019-07-16T06:44:55Z', + owner: {name: 'admin'}, + writable: '1', + in_use: '0', + report_format: { + _id: '54321', + name: 'baz', + }, +}); + +const caps = new Capabilities(['everything']); +const wrongCaps = new Capabilities(['get_config']); + +const reloadInterval = 1; +const manualUrl = 'test/'; + +const currentSettings = jest.fn().mockResolvedValue({foo: 'bar'}); + +const getFilters = jest.fn().mockReturnValue( + Promise.resolve({ + data: [], + meta: { + filter: Filter.fromString(), + counts: new CollectionCounts(), + }, + }), +); + +const getReportConfigs = jest.fn().mockResolvedValue({ + data: [config], + meta: { + filter: Filter.fromString(), + counts: new CollectionCounts(), + }, +}); + +const getSetting = jest.fn().mockResolvedValue({filter: null}); + +describe('ReportConfigsPage tests', () => { + test('should render full ReportConfigsPage', async () => { + const gmp = { + reportconfigs: { + get: getReportConfigs, + }, + filters: { + get: getFilters, + }, + reloadInterval, + settings: {manualUrl}, + user: {currentSettings, getSetting: getSetting}, + }; + + const {render, store} = rendererWith({ + gmp, + capabilities: true, + store: true, + router: true, + }); + + store.dispatch(setUsername('admin')); + + const defaultSettingfilter = Filter.fromString('foo=bar'); + store.dispatch(loadingActions.success({rowsperpage: {value: '2'}})); + store.dispatch( + defaultFilterLoadingActions.success('reportconfig', defaultSettingfilter), + ); + + const counts = new CollectionCounts({ + first: 1, + all: 1, + filtered: 1, + length: 1, + rows: 10, + }); + const filter = Filter.fromString('first=1 rows=10'); + const loadedFilter = Filter.fromString('first=1 rows=10'); + store.dispatch( + entitiesLoadingActions.success([config], filter, loadedFilter, counts), + ); + + const {baseElement} = render(); + + await waitFor(() => baseElement.querySelectorAll('table')); + + expect(baseElement).toMatchSnapshot(); + }); + + test('should call commands for bulk actions', async () => { + const deleteByFilter = jest.fn().mockResolvedValue({ + foo: 'bar', + }); + + const addTagByFilter = jest.fn().mockResolvedValue({ + foo: 'bar', + }); + + const renewSession = jest.fn().mockResolvedValue({data: {}}); + + const gmp = { + reportconfigs: { + get: getReportConfigs, + deleteByFilter, + addTagByFilter, + }, + filters: { + get: getFilters, + }, + reloadInterval, + settings: {manualUrl}, + user: {currentSettings, getSetting, renewSession}, + }; + + const {render, store} = rendererWith({ + gmp, + capabilities: true, + store: true, + router: true, + }); + + store.dispatch(setUsername('admin')); + + const defaultSettingfilter = Filter.fromString('foo=bar'); + store.dispatch(loadingActions.success({rowsperpage: {value: '2'}})); + store.dispatch( + defaultFilterLoadingActions.success('reportconfig', defaultSettingfilter), + ); + + const counts = new CollectionCounts({ + first: 1, + all: 1, + filtered: 1, + length: 1, + rows: 10, + }); + const filter = Filter.fromString('first=1 rows=10'); + const loadedFilter = Filter.fromString('first=1 rows=10'); + store.dispatch( + entitiesLoadingActions.success([config], filter, loadedFilter, counts), + ); + + const {baseElement, getAllByTestId} = render(); + + await waitFor(() => baseElement.querySelectorAll('table')); + + const icons = getAllByTestId('svg-icon'); + + await act(async () => { + expect(icons[17]).toHaveAttribute('title', 'Add tag to page contents'); + + expect(icons[18]).toHaveAttribute( + 'title', + 'Move page contents to trashcan', + ); + fireEvent.click(icons[18]); + expect(deleteByFilter).toHaveBeenCalled(); + }); + }); + + describe('ReportConfigsPage ToolBarIcons test', () => { + test('should render', () => { + const handleReportConfigCreateClick = jest.fn(); + + const gmp = { + settings: {manualUrl}, + }; + + const {render} = rendererWith({ + gmp, + capabilities: caps, + router: true, + }); + + const {element, getAllByTestId} = render( + , + ); + expect(element).toMatchSnapshot(); + + const icons = getAllByTestId('svg-icon'); + const links = element.querySelectorAll('a'); + + expect(icons[0]).toHaveAttribute('title', 'Help: Report Configs'); + expect(links[0]).toHaveAttribute( + 'href', + 'test/en/reports.html#managing-report-configs', + ); + }); + + test('should call click handlers', () => { + const handleReportConfigCreateClick = jest.fn(); + + const gmp = { + settings: {manualUrl}, + }; + + const {render} = rendererWith({ + gmp, + capabilities: caps, + router: true, + }); + + const {getAllByTestId} = render( + , + ); + + const icons = getAllByTestId('svg-icon'); + + fireEvent.click(icons[1]); + expect(handleReportConfigCreateClick).toHaveBeenCalled(); + expect(icons[1]).toHaveAttribute('title', 'New Report Config'); + }); + + test('should not show icons if user does not have the right permissions', () => { + const handleReportConfigCreateClick = jest.fn(); + + const gmp = {settings: {manualUrl}}; + + const {render} = rendererWith({ + gmp, + capabilities: wrongCaps, + router: true, + }); + + const {queryAllByTestId} = render( + , + ); + + const icons = queryAllByTestId('svg-icon'); + expect(icons.length).toBe(1); + expect(icons[0]).toHaveAttribute('title', 'Help: Report Configs'); + }); + }); +}); diff --git a/src/web/pages/reportconfigs/__tests__/row.js b/src/web/pages/reportconfigs/__tests__/row.js new file mode 100644 index 0000000000..067636462b --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/row.js @@ -0,0 +1,411 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +/* eslint-disable no-unused-vars */ +/* eslint-disable no-console */ +import React from 'react'; + +import Capabilities from 'gmp/capabilities/capabilities'; + +import ReportConfig from 'gmp/models/reportconfig'; + +import {setUsername} from 'web/store/usersettings/actions'; + +import {rendererWith, fireEvent} from 'web/utils/testing'; + +import Row from '../row'; + +const gmp = {settings: {}}; +const caps = new Capabilities(['everything']); + +const entity = ReportConfig.fromElement({ + _id: '1234', + name: 'foo', + comment: 'bar', + owner: {name: 'admin'}, + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '4321', + name: 'baz', + }, +}); + +const orphanEntity = ReportConfig.fromElement({ + _id: '1234', + name: 'foo', + comment: 'bar', + owner: {name: 'admin'}, + permissions: {permission: [{name: 'everything'}]}, + orphan: '1', + report_format: { + _id: '4321', + }, +}); + +describe('Report Config row tests', () => { + // deactivate console.error for tests + // to make it possible to test a row without a table + const consoleError = console.error; + console.error = () => {}; + + test('should render', () => { + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const {render} = rendererWith({ + gmp, + capabilities: caps, + store: true, + router: true, + }); + + const {baseElement, getAllByTestId} = render( + , + ); + + expect(baseElement).toMatchSnapshot(); + expect(baseElement).toHaveTextContent('foo'); + expect(baseElement).toHaveTextContent('(bar)'); + expect(baseElement).toHaveTextContent('baz'); + }); + + test('should render orphan', () => { + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const {render} = rendererWith({ + gmp, + capabilities: caps, + store: true, + router: true, + }); + + const {baseElement, getAllByTestId} = render( + , + ); + + expect(baseElement).toMatchSnapshot(); + expect(baseElement).toHaveTextContent('foo'); + expect(baseElement).toHaveTextContent('Orphan'); + expect(baseElement).toHaveTextContent('(bar)'); + expect(baseElement).toHaveTextContent('4321'); + }); + + test('should render observer icon', () => { + const config = ReportConfig.fromElement({ + _id: '1234', + name: 'foo', + comment: 'bar', + in_use: '0', + writable: '1', + owner: { + name: 'user', + }, + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '54321', + name: 'baz', + }, + }); + + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const {render, store} = rendererWith({ + gmp, + capabilities: caps, + store: true, + router: true, + }); + + store.dispatch(setUsername('admin')); + + const {getAllByTestId} = render( + , + ); + + const icons = getAllByTestId('svg-icon'); + expect(icons[0]).toHaveAttribute('title', 'Report Config owned by user'); + }); + + test('should call click handlers', () => { + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const {render} = rendererWith({ + gmp, + capabilities: true, + store: true, + router: true, + }); + + const {baseElement, getAllByTestId} = render( + , + ); + + const spans = baseElement.querySelectorAll('span'); + fireEvent.click(spans[1]); + expect(handleToggleDetailsClick).toHaveBeenCalledWith(undefined, '1234'); + + const icons = getAllByTestId('svg-icon'); + + expect(icons[1]).toHaveAttribute('title', 'Move Report Config to trashcan'); + fireEvent.click(icons[1]); + expect(handleReportConfigDelete).toHaveBeenCalledWith(entity); + + expect(icons[2]).toHaveAttribute('title', 'Edit Report Config'); + fireEvent.click(icons[2]); + expect(handleReportConfigEdit).toHaveBeenCalledWith(entity); + + expect(icons[3]).toHaveAttribute('title', 'Clone Report Config'); + fireEvent.click(icons[3]); + expect(handleReportConfigClone).toHaveBeenCalledWith(entity); + + expect(icons[4]).toHaveAttribute('title', 'Export Report Config'); + fireEvent.click(icons[4]); + expect(handleReportConfigDownload).toHaveBeenCalledWith(entity); + }); + + test('should not call click handlers without permissions', () => { + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const config = ReportConfig.fromElement({ + _id: '1234', + name: 'foo', + comment: 'bar', + in_use: '0', + writable: '1', + report_format: { + _id: '54321', + name: 'baz', + }, + }); + + const {render} = rendererWith({ + gmp, + capabilities: caps, + store: true, + router: true, + }); + + const {baseElement, getAllByTestId} = render( + , + ); + + const spans = baseElement.querySelectorAll('span'); + fireEvent.click(spans[1]); + expect(handleToggleDetailsClick).toHaveBeenCalledWith(undefined, '1234'); + + const icons = getAllByTestId('svg-icon'); + + fireEvent.click(icons[0]); + expect(handleReportConfigDelete).not.toHaveBeenCalled(); + expect(icons[0]).toHaveAttribute( + 'title', + 'Permission to move Report Config to trashcan denied', + ); + + fireEvent.click(icons[1]); + expect(handleReportConfigEdit).not.toHaveBeenCalled(); + expect(icons[1]).toHaveAttribute( + 'title', + 'Permission to edit Report Config denied', + ); + + fireEvent.click(icons[2]); + expect(handleReportConfigClone).not.toHaveBeenCalled(); + expect(icons[2]).toHaveAttribute( + 'title', + 'Permission to clone Report Config denied', + ); + + fireEvent.click(icons[3]); + expect(handleReportConfigDownload).toHaveBeenCalledWith(config); + expect(icons[3]).toHaveAttribute('title', 'Export Report Config'); + }); + + test('should (not) call click handlers if scan config is in use', () => { + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const config = ReportConfig.fromElement({ + _id: '1234', + name: 'foo', + comment: 'bar', + in_use: '1', + writable: '1', + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '54321', + name: 'baz', + }, + }); + + const {render} = rendererWith({ + gmp, + capabilities: true, + store: true, + router: true, + }); + + const {baseElement, getAllByTestId} = render( + , + ); + + const spans = baseElement.querySelectorAll('span'); + fireEvent.click(spans[1]); + expect(handleToggleDetailsClick).toHaveBeenCalledWith(undefined, '1234'); + + const icons = getAllByTestId('svg-icon'); + + fireEvent.click(icons[0]); + expect(handleReportConfigDelete).not.toHaveBeenCalled(); + expect(icons[0]).toHaveAttribute('title', 'Report Config is still in use'); + + fireEvent.click(icons[1]); + expect(handleReportConfigEdit).toHaveBeenCalledWith(config); + expect(icons[1]).toHaveAttribute('title', 'Edit Report Config'); + + fireEvent.click(icons[2]); + expect(handleReportConfigClone).toHaveBeenCalledWith(config); + expect(icons[2]).toHaveAttribute('title', 'Clone Report Config'); + + fireEvent.click(icons[3]); + expect(handleReportConfigDownload).toHaveBeenCalledWith(config); + expect(icons[3]).toHaveAttribute('title', 'Export Report Config'); + }); + + test('should (not) call click handlers if scan config is not writable', () => { + const handleToggleDetailsClick = jest.fn(); + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const config = ReportConfig.fromElement({ + _id: '1234', + name: 'foo', + comment: 'bar', + in_use: '0', + writable: '0', + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '54321', + name: 'baz', + }, + }); + + const {render} = rendererWith({ + gmp, + capabilities: true, + store: true, + router: true, + }); + + const {baseElement, getAllByTestId} = render( + , + ); + + const spans = baseElement.querySelectorAll('span'); + fireEvent.click(spans[1]); + expect(handleToggleDetailsClick).toHaveBeenCalledWith(undefined, '1234'); + + const icons = getAllByTestId('svg-icon'); + + fireEvent.click(icons[1]); + expect(handleReportConfigDelete).not.toHaveBeenCalled(); + expect(icons[1]).toHaveAttribute('title', 'Report Config is not writable'); + + fireEvent.click(icons[2]); + expect(handleReportConfigClone).toHaveBeenCalledWith(config); + expect(icons[2]).toHaveAttribute('title', 'Clone Report Config'); + + fireEvent.click(icons[3]); + expect(handleReportConfigDownload).toHaveBeenCalledWith(config); + expect(icons[3]).toHaveAttribute('title', 'Export Report Config'); + }); + + console.warn = consoleError; +}); diff --git a/src/web/pages/reportconfigs/__tests__/table.js b/src/web/pages/reportconfigs/__tests__/table.js new file mode 100644 index 0000000000..7defc7781a --- /dev/null +++ b/src/web/pages/reportconfigs/__tests__/table.js @@ -0,0 +1,204 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import React from 'react'; + +import CollectionCounts from 'gmp/collection/collectioncounts'; + +import Filter from 'gmp/models/filter'; +import ReportConfig from 'gmp/models/reportconfig'; + +import {setUsername} from 'web/store/usersettings/actions'; + +import {rendererWith, fireEvent} from 'web/utils/testing'; + +import Table from '../table'; + +const config = ReportConfig.fromElement({ + _id: '12345', + name: 'foo', + comment: 'bar', + owner: {name: 'admin'}, + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '54321', + name: 'baz', + }, +}); + +const config2 = ReportConfig.fromElement({ + _id: '123456', + name: 'lorem', + comment: 'ipsum', + owner: {name: 'admin'}, + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '654321', + name: 'dolor', + }, +}); + +const config3 = ReportConfig.fromElement({ + _id: '1234567', + name: 'hello', + comment: 'world', + owner: {name: 'admin'}, + permissions: {permission: [{name: 'everything'}]}, + report_format: { + _id: '54321', + name: 'baz', + }, +}); + +const counts = new CollectionCounts({ + first: 1, + all: 1, + filtered: 1, + length: 1, + rows: 2, +}); + +const filter = Filter.fromString('rows=2'); + +describe('Scan Config table tests', () => { + test('should render', () => { + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const gmp = { + settings: {}, + }; + + const {render, store} = rendererWith({ + gmp, + capabilities: true, + store: true, + router: true, + }); + + store.dispatch(setUsername('admin')); + + const {baseElement} = render( + , + ); + + expect(baseElement).toMatchSnapshot(); + const header = baseElement.querySelectorAll('th'); + expect(header[0]).toHaveTextContent('Name'); + expect(header[1]).toHaveTextContent('Report Format'); + expect(header[2]).toHaveTextContent('Actions'); + }); + + test('should unfold all details', () => { + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const gmp = { + settings: {}, + }; + + const {render, store} = rendererWith({ + gmp, + capabilities: true, + router: true, + store: true, + }); + + store.dispatch(setUsername('admin')); + + const {element, getAllByTestId} = render( +
, + ); + + expect(element).not.toHaveTextContent('Parameters'); + + const icons = getAllByTestId('svg-icon'); + fireEvent.click(icons[0]); + expect(icons[0]).toHaveAttribute('title', 'Unfold all details'); + expect(element).toHaveTextContent('Parameters'); + }); + + test('should call click handlers', () => { + const handleReportConfigClone = jest.fn(); + const handleReportConfigDelete = jest.fn(); + const handleReportConfigDownload = jest.fn(); + const handleReportConfigEdit = jest.fn(); + + const gmp = { + settings: {}, + }; + + const {render, store} = rendererWith({ + gmp, + capabilities: true, + router: true, + store: true, + }); + + store.dispatch(setUsername('admin')); + + const {getAllByTestId} = render( +
, + ); + + const icons = getAllByTestId('svg-icon'); + + expect(icons[5]).toHaveAttribute('title', 'Move Report Config to trashcan'); + fireEvent.click(icons[5]); + expect(handleReportConfigDelete).toHaveBeenCalledWith(config); + + expect(icons[6]).toHaveAttribute('title', 'Edit Report Config'); + fireEvent.click(icons[6]); + expect(handleReportConfigEdit).toHaveBeenCalledWith(config); + + expect(icons[7]).toHaveAttribute('title', 'Clone Report Config'); + fireEvent.click(icons[7]); + expect(handleReportConfigClone).toHaveBeenCalledWith(config); + + expect(icons[8]).toHaveAttribute('title', 'Export Report Config'); + fireEvent.click(icons[8]); + expect(handleReportConfigDownload).toHaveBeenCalledWith(config); + }); +}); diff --git a/src/web/pages/reportconfigs/component.js b/src/web/pages/reportconfigs/component.js new file mode 100644 index 0000000000..f97e91e90c --- /dev/null +++ b/src/web/pages/reportconfigs/component.js @@ -0,0 +1,198 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import React from 'react'; + +import _ from 'gmp/locale'; + +import {isDefined} from 'gmp/utils/identity'; + +import PropTypes from 'web/utils/proptypes'; +import withGmp from 'web/utils/withGmp'; + +import EntityComponent from 'web/entity/component'; + +import ReportConfigDialog from './dialog'; + +class ReportConfigComponent extends React.Component { + constructor(...args) { + super(...args); + + this.state = {dialogVisible: false}; + + this.handleCloseReportConfigDialog = + this.handleCloseReportConfigDialog.bind(this); + this.handleSave = this.handleSave.bind(this); + this.openReportConfigDialog = this.openReportConfigDialog.bind(this); + } + + openReportConfigDialog(reportconfig) { + this.handleInteraction(); + const {gmp} = this.props; + + if (isDefined(reportconfig)) { + // (re-)load report config to get params + gmp.reportconfig.get(reportconfig).then(response => { + const config = response.data; + const preferences = {}; + const id_lists = {}; + + config.params.forEach(param => { + preferences[param.name] = param.value; + }); + + const p2 = gmp.reportformats.getAll().then(resp => resp.data); + + p2.then(formats => { + this.setState({ + id: config.id, + name: config.name, + comment: config.comment, + dialogVisible: true, + formats, + id_lists, + preferences, + reportconfig: config, + originalParamInfo: reportconfig.params, + title: _('Edit Report Config {{name}}', {name: config.name}), + }); + }); + }); + } else { + gmp.reportformats + .getAll() + .then(resp => resp.data) + .then(formats => { + this.setState({ + dialogVisible: true, + reportconfig: undefined, + formats, + title: _('New Report Config'), + }); + }); + } + } + + closeReportConfigDialog() { + this.setState({dialogVisible: false}); + } + + handleCloseReportConfigDialog() { + this.closeReportConfigDialog(); + this.handleInteraction(); + } + + handleSave(data) { + const {gmp} = this.props; + + this.handleInteraction(); + + if (isDefined(data.id)) { + const {onSaved, onSaveError} = this.props; + return gmp.reportconfig + .save(data) + .then(onSaved, onSaveError) + .then(() => this.closeReportConfigDialog()); + } + + const {onCreated, onCreateError} = this.props; + return gmp.reportconfig + .create(data) + .then(onCreated, onCreateError) + .then(() => this.closeReportConfigDialog()); + } + + handleInteraction() { + const {onInteraction} = this.props; + if (isDefined(onInteraction)) { + onInteraction(); + } + } + + render() { + const { + children, + onCloneError, + onCloned, + onCreateError, + onCreated, + onDeleteError, + onDeleted, + onDownloadError, + onDownloaded, + onInteraction, + } = this.props; + + const {dialogVisible, formats, reportconfig, title, preferences} = + this.state; + + return ( + + {other => ( + + {children({ + ...other, + create: this.openReportConfigDialog, + edit: this.openReportConfigDialog, + })} + {dialogVisible && ( + + )} + + )} + + ); + } +} + +ReportConfigComponent.propTypes = { + children: PropTypes.func.isRequired, + gmp: PropTypes.gmp.isRequired, + onCloneError: PropTypes.func, + onCloned: PropTypes.func, + onCreateError: PropTypes.func, + onCreated: PropTypes.func, + onDeleteError: PropTypes.func, + onDeleted: PropTypes.func, + onDownloadError: PropTypes.func, + onDownloaded: PropTypes.func, + onInteraction: PropTypes.func.isRequired, + onSaveError: PropTypes.func, + onSaved: PropTypes.func, +}; + +export default withGmp(ReportConfigComponent); + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reportconfigs/details.js b/src/web/pages/reportconfigs/details.js new file mode 100644 index 0000000000..cb360fbf83 --- /dev/null +++ b/src/web/pages/reportconfigs/details.js @@ -0,0 +1,161 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; + +import _ from 'gmp/locale'; + +import PropTypes from 'web/utils/proptypes'; + +import Layout from 'web/components/layout/layout'; + +import DetailsLink from 'web/components/link/detailslink'; + +import InfoTable from 'web/components/table/infotable'; +import TableBody from 'web/components/table/body'; +import TableData, {TableDataAlignTop} from 'web/components/table/data'; +import TableRow from 'web/components/table/row'; + +import {Col} from 'web/entity/page'; +import {map} from 'gmp/utils/array'; +import {isDefined} from 'gmp/utils/identity'; +import {renderYesNo} from 'web/utils/render'; + +export const ReportConfigParamValue = ({ + param, + value = param.value, + value_labels = param.value_labels, + links = true, +}) => { + if (param.type === 'report_format_list') { + return map(value, report_format_id => { + const label = isDefined(value_labels[report_format_id]) + ? value_labels[report_format_id] + : report_format_id; + return ( + + {label} + + ); + }); + } else if (param.type === 'text') { + return
{value}
; + } else if (param.type === 'boolean') { + return renderYesNo(value); + } + + return value; +}; + +ReportConfigParamValue.propTypes = { + links: PropTypes.bool, + param: PropTypes.any.isRequired, + value: PropTypes.any, + value_labels: PropTypes.object, +}; + +const ReportConfigDetails = ({entity, links = true}) => { + const {orphan, report_format, params, alerts = []} = entity; + + const reportFormatLink = orphan ? ( + report_format.id + ) : ( + + {report_format.name} + + ); + const paramRows = orphan ? ( + + + {_('not available for orphaned report configs')} + + + ) : ( + params.map(param => { + return ( + + {param.name} + + + + + ); + }) + ); + + return ( + + +
+ + + + + + {_('Report Format')} + + {reportFormatLink} + + + + + {_('Parameters')} + + + + {paramRows} + + + + + + {alerts.length > 0 && ( + + + {_('Alerts using this Report Config')} + + + {alerts.map(alert => ( + + + {alert.name} + + + ))} + + + )} + + + + ); +}; + +ReportConfigDetails.propTypes = { + entity: PropTypes.model.isRequired, + links: PropTypes.bool, +}; + +export default ReportConfigDetails; + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reportconfigs/detailspage.js b/src/web/pages/reportconfigs/detailspage.js new file mode 100644 index 0000000000..5a36fa8fd1 --- /dev/null +++ b/src/web/pages/reportconfigs/detailspage.js @@ -0,0 +1,308 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; + +import _ from 'gmp/locale'; + +import CreateIcon from 'web/entity/icon/createicon'; +import TrashIcon from 'web/entity/icon/trashicon'; +import Divider from 'web/components/layout/divider'; +import EditIcon from 'web/entity/icon/editicon'; +import IconDivider from 'web/components/layout/icondivider'; +import Layout from 'web/components/layout/layout'; +import PageTitle from 'web/components/layout/pagetitle'; +import ListIcon from 'web/components/icon/listicon'; +import ManualIcon from 'web/components/icon/manualicon'; +import ReportConfigIcon from 'web/components/icon/reportconfigicon'; + +import Tab from 'web/components/tab/tab'; +import TabLayout from 'web/components/tab/tablayout'; +import TabList from 'web/components/tab/tablist'; +import TabPanel from 'web/components/tab/tabpanel'; +import TabPanels from 'web/components/tab/tabpanels'; +import Tabs from 'web/components/tab/tabs'; + +import Table from 'web/components/table/stripedtable'; +import TableBody from 'web/components/table/body'; +import TableData from 'web/components/table/data'; +import TableHeader from 'web/components/table/header'; +import TableHead from 'web/components/table/head'; +import TableRow from 'web/components/table/row'; + +import EntityPage from 'web/entity/page'; +import {goto_details, goto_list} from 'web/entity/component'; +import EntityPermissions from 'web/entity/permissions'; +import EntitiesTab from 'web/entity/tab'; +import EntityTags from 'web/entity/tags'; +import withEntityContainer, { + permissionsResourceFilter, +} from 'web/entity/withEntityContainer'; + +import {selector, loadEntity} from 'web/store/entities/reportconfigs'; + +import { + selector as permissionsSelector, + loadEntities as loadPermissions, +} from 'web/store/entities/permissions'; + +import {renderYesNo} from 'web/utils/render'; +import PropTypes from 'web/utils/proptypes'; +import withCapabilities from 'web/utils/withCapabilities'; + +import ReportConfigComponent from './component'; +import ReportConfigDetails, {ReportConfigParamValue} from './details'; +import CloneIcon from 'web/entity/icon/cloneicon'; + +const ToolBarIcons = withCapabilities( + ({ + capabilities, + entity, + onReportConfigCloneClick, + onReportConfigCreateClick, + onReportConfigDeleteClick, + onReportConfigEditClick, + }) => ( + + + + + + + + + + + + + ), +); + +ToolBarIcons.propTypes = { + entity: PropTypes.model.isRequired, + onReportConfigCreateClick: PropTypes.func.isRequired, + onReportConfigDeleteClick: PropTypes.func.isRequired, + onReportConfigEditClick: PropTypes.func.isRequired, +}; + +const Details = ({entity, links = true}) => { + return ( + + + + ); +}; + +Details.propTypes = { + entity: PropTypes.model.isRequired, + links: PropTypes.bool, +}; + +const Parameters = ({entity}) => { + const {params = []} = entity; + return ( + + {params.length === 0 && _('No parameters available')} + {params.length > 0 && ( +
+ + + {_('Name')} + {_('Value')} + {_('Using Default')} + {_('Default Value')} + {_('Minimum')} + {_('Maximum')} + + + + {params.map(param => ( + + {param.name} + + + + {renderYesNo(param.value_using_default)} + + + + {param.min} + {param.max} + + ))} + +
+ )} + + ); +}; + +Parameters.propTypes = { + entity: PropTypes.model.isRequired, +}; + +const Page = ({ + entity, + links = true, + permissions = [], + onChanged, + onError, + onInteraction, + ...props +}) => ( + + {({clone, delete: delete_func, edit, create: create_func, save}) => ( + } + title={_('Report Config')} + toolBarIcons={ToolBarIcons} + onInteraction={onInteraction} + onReportConfigCreateClick={create_func} + onReportConfigDeleteClick={delete_func} + onReportConfigEditClick={edit} + onReportConfigSaveClick={save} + onReportConfigCloneClick={clone} + > + {({activeTab = 0, onActivateTab}) => { + return ( + + + + + + {_('Information')} + + {_('Parameter Details')} + + + {_('User Tags')} + + + {_('Permissions')} + + + + + + + +
+ + + + + + + + + + + + + + + ); + }} + + )} + +); + +Page.propTypes = { + entity: PropTypes.model, + links: PropTypes.bool, + permissions: PropTypes.array, + onChanged: PropTypes.func.isRequired, + onError: PropTypes.func.isRequired, + onInteraction: PropTypes.func.isRequired, +}; + +const load = gmp => { + const loadEntityFunc = loadEntity(gmp); + const loadPermissionsFunc = loadPermissions(gmp); + return id => dispatch => + Promise.all([ + dispatch(loadEntityFunc(id)), + dispatch(loadPermissionsFunc(permissionsResourceFilter(id))), + ]); +}; + +const mapStateToProps = (rootState, {id}) => { + const permissionsSel = permissionsSelector(rootState); + return { + permissions: permissionsSel.getEntities(permissionsResourceFilter(id)), + }; +}; + +export default withEntityContainer('reportconfig', { + entitySelector: selector, + load, + mapStateToProps, +})(Page); + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reportconfigs/dialog.js b/src/web/pages/reportconfigs/dialog.js new file mode 100644 index 0000000000..f3ff8b95bb --- /dev/null +++ b/src/web/pages/reportconfigs/dialog.js @@ -0,0 +1,396 @@ +/* Copyright (C) 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; + +import _ from 'gmp/locale'; + +import {isDefined} from 'gmp/utils/identity'; +import {map} from 'gmp/utils/array'; +import {parseBoolean} from 'gmp/parser'; + +import PropTypes from 'web/utils/proptypes'; +import withGmp from 'web/utils/withGmp'; + +import SaveDialog from 'web/components/dialog/savedialog'; + +import FormGroup from 'web/components/form/formgroup'; +import Spinner from 'web/components/form/spinner'; +import TextArea from 'web/components/form/textarea'; +import TextField from 'web/components/form/textfield'; +import MultiSelect from 'web/components/form/multiselect'; +import Select from 'web/components/form/select'; +import YesNoRadio from 'web/components/form/yesnoradio'; + +import Layout from 'web/components/layout/layout'; + +import Table from 'web/components/table/table'; +import TableBody from 'web/components/table/body'; +import TableData from 'web/components/table/data'; +import TableHeader from 'web/components/table/header'; +import TableHead from 'web/components/table/head'; +import TableRow from 'web/components/table/row'; +import Checkbox from 'web/components/form/checkbox'; + +const Param = ({ + data, + usingDefaultData, + value, + onPrefChange, + onParamUsingDefaultChange, + formats, +}) => { + const {name, type, min, max} = value; + const field_value = data[name]; + + const formatOptions = map(formats, format => ({ + label: format.name, + value: format.id, + })); + + let field; + if (type === 'boolean') { + field = ( + + ); + } else if (type === 'integer') { + field = ( + + ); + } else if (type === 'string') { + field = ( + + ); + } else if (type === 'selection') { + const typeOptions = map(value.options, opt => ({ + label: opt.name, + value: opt.value, + })); + + field = ( +