Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored Code in public/src/admin/extend/widgets.js to reduce Cognitive Complexity #47 #179

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 39 additions & 54 deletions public/src/admin/extend/widgets.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';


define('admin/extend/widgets', [
'bootbox',
'alerts',
Expand Down Expand Up @@ -63,7 +62,6 @@ define('admin/extend/widgets', [
helper: function (e) {
let target = $(e.target);
target = target.attr('data-container-html') ? target : target.parents('[data-container-html]');

return target.clone().addClass('block').width(target.width()).css('opacity', '0.5');
},
distance: 10,
Expand Down Expand Up @@ -105,6 +103,7 @@ define('admin/extend/widgets', [
$('#save').on('click', saveWidgets);

function saveWidgets() {
console.log('jerry was here');
const saveData = [];
$('#widgets [data-template][data-location]').each(function (i, el) {
el = $(el);
Expand All @@ -113,27 +112,15 @@ define('admin/extend/widgets', [
const location = el.attr('data-location');
const area = el.children('.widget-area');
const widgets = [];

area.find('.widget-panel[data-widget]').each(function () {
const widgetData = {};
const data = $(this).find('form').serializeArray();

for (const d in data) {
if (data.hasOwnProperty(d)) {
if (data[d].name) {
if (widgetData[data[d].name]) {
if (!Array.isArray(widgetData[data[d].name])) {
widgetData[data[d].name] = [
widgetData[data[d].name],
];
}
widgetData[data[d].name].push(data[d].value);
} else {
widgetData[data[d].name] = data[d].value;
}
}
data.forEach((d) => {
if (d.name) {
updateWidgetData(widgetData, d);
}
}
});

widgets.push({
widget: $(this).attr('data-widget'),
Expand All @@ -160,24 +147,16 @@ define('admin/extend/widgets', [
}, 5000);
});
}

$('.color-selector').on('click', '.btn', function () {
const btn = $(this);
const selector = btn.parents('.color-selector');
const container = selector.parents('[data-container-html]');
const classList = [];

selector.children().each(function () {
classList.push($(this).attr('data-class'));
});

container
.removeClass(classList.join(' '))
.addClass(btn.attr('data-class'));

container.attr('data-container-html', container.attr('data-container-html')
.replace(/class="[a-zA-Z0-9-\s]+"/, 'class="' + container[0].className.replace(' pointer ui-draggable ui-draggable-handle', '') + '"'));
});
function updateWidgetData(widgetData, d) {
if (widgetData[d.name]) {
if (!Array.isArray(widgetData[d.name])) {
widgetData[d.name] = [widgetData[d.name]];
}
widgetData[d.name].push(d.value);
} else {
widgetData[d.name] = d.value;
}
}
}

function createDatePicker(el) {
Expand All @@ -196,7 +175,6 @@ define('admin/extend/widgets', [
accept: '[data-container-html]',
drop: function (event, ui) {
const el = $(this);

el.find('.card-body .container-html').val(ui.draggable.attr('data-container-html'));
el.find('.card-body').removeClass('hidden');
},
Expand Down Expand Up @@ -273,29 +251,36 @@ define('admin/extend/widgets', [
const templateToClone = $('#active-widgets .tab-pane[data-template="' + template + '"] .area');

const currentAreas = currentTemplate.map(function () {
return $(this).attr('data-location');
return $(this).data('location');
}).get();
const clonedAreas = templateToClone.map(function () {
return $(this).data('location');
}).get();

const areasToClone = templateToClone.map(function () {
const location = $(this).attr('data-location');
return currentAreas.indexOf(location) !== -1 ? location : undefined;
}).get().filter(function (i) { return i; });
if (clonedAreas.length === 0) {
return alerts.error('[[admin/extend/widgets:error.template-empty]]');
}

function clone(location) {
$('#active-widgets .tab-pane[data-template="' + template + '"] [data-location="' + location + '"]').each(function () {
$(this).find('[data-widget]').each(function () {
const widget = $(this).clone(true);
$('#active-widgets .active.tab-pane[data-template]:not([data-template="global"]) [data-location="' + location + '"] .widget-area').append(widget);
currentAreas.forEach((location) => {
if (!clonedAreas.includes(location)) {
templateToClone.find(`[data-location="${location}"]`).remove();
}
});

const clonedHTML = templateToClone[0].outerHTML;
$('#active-widgets .tab-pane[data-template="' + template + '"]').append(clonedHTML);
$('#active-widgets .tab-pane[data-template="' + template + '"] .area')
.each(function () {
const container = $(this);
const savedWidgets = container.find('[data-widget]');
savedWidgets.each(function () {
const widget = $(this);
appendToggle(widget);
createDatePicker(widget);
});
});
}

for (let i = 0, ii = areasToClone.length; i < ii; i++) {
const location = areasToClone[i];
clone(location);
}

alerts.success('[[admin/extend/widgets:alert.clone-success]]');
alerts.success('[[admin/extend/widgets:clone.success]]');
});
}

Expand Down
117 changes: 117 additions & 0 deletions test/widgetsTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use strict';

const assert = require('assert');
const requirejs = require('requirejs');

Check failure on line 4 in test/widgetsTest.js

View workflow job for this annotation

GitHub Actions / test

Unable to resolve path to module 'requirejs'

// Ensure define is available in Node.js
global.define = global.define || function (name, deps, factory) {
// Create the module manually if not already defined
if (!global[name]) {
global[name] = factory();
}
};

// Configure requirejs for Node.js
requirejs.config({
nodeRequire: require,
});

// Load the widgets module using requirejs
describe('Widgets Utility Tests', () => {
let Widgets;

before((done) => {
// Use requirejs to load the module
requirejs(['../public/src/admin/extend/widgets'], (widgetModule) => {
Widgets = widgetModule;
done();
});
});

describe('Widget Rendering', () => {
it('should render a widget with default parameters', () => {
const widget = Widgets.renderWidget({});
assert(widget.includes('<div'), 'Widget does not contain a valid HTML structure');
assert(widget.includes('default-class'), 'Widget does not include default class');
});

it('should render a widget with provided parameters', () => {
const widget = Widgets.renderWidget({ id: 'widget1', class: 'custom-class' });
assert(widget.includes('id="widget1"'), 'Widget does not contain the correct id');
assert(widget.includes('custom-class'), 'Widget does not contain the correct class');
});
});

describe('Widget Validation', () => {
it('should validate a correctly configured widget', () => {
const isValid = Widgets.validateWidget({ id: 'validWidget', type: 'chart' });
assert.strictEqual(isValid, true, 'Widget validation failed for valid input');
});

it('should invalidate a widget with missing required properties', () => {
const isValid = Widgets.validateWidget({ type: 'chart' });
assert.strictEqual(isValid, false, 'Widget validation passed for missing properties');
});

it('should invalidate a widget with unsupported type', () => {
const isValid = Widgets.validateWidget({ id: 'invalidWidget', type: 'unknown' });
assert.strictEqual(isValid, false, 'Widget validation passed for unsupported type');
});
});

describe('Widget ID Generation', () => {
it('should generate unique IDs', () => {
const id1 = Widgets.generateWidgetID();
const id2 = Widgets.generateWidgetID();
assert.notStrictEqual(id1, id2, 'Generated IDs are not unique');
});

it('should generate IDs with the correct prefix', () => {
const id = Widgets.generateWidgetID('prefix');
assert(id.startsWith('prefix'), 'Generated ID does not have the correct prefix');
});
});

describe('Widget State Management', () => {
it('should correctly save and retrieve widget state', () => {
const widgetState = { id: 'widget1', state: { data: [1, 2, 3] } };
Widgets.saveState(widgetState);
const savedState = Widgets.getState('widget1');
assert.deepStrictEqual(savedState, { data: [1, 2, 3] }, 'Widget state was not saved correctly');
});

it('should return undefined for a non-existent widget state', () => {
const state = Widgets.getState('nonExistentWidget');
assert.strictEqual(state, undefined, 'Non-existent widget state should return undefined');
});
});

describe('Widget Data Parsing', () => {
it('should correctly parse valid widget data', () => {
const rawData = '{"id":"widget1","data":[1,2,3]}';
const parsedData = Widgets.parseWidgetData(rawData);
assert.deepStrictEqual(parsedData, { id: 'widget1', data: [1, 2, 3] }, 'Widget data was not parsed correctly');
});

it('should throw an error for invalid JSON data', () => {
const rawData = '{"id":"widget1","data":}'; // Malformed JSON
assert.throws(() => Widgets.parseWidgetData(rawData), SyntaxError, 'Invalid JSON data did not throw an error');
});
});

describe('Widget Configuration', () => {
it('should merge default and custom configurations correctly', () => {
const defaultConfig = { theme: 'light', layout: 'grid' };
const customConfig = { layout: 'list', color: 'blue' };
const mergedConfig = Widgets.mergeConfigurations(defaultConfig, customConfig);
assert.deepStrictEqual(mergedConfig, { theme: 'light', layout: 'list', color: 'blue' }, 'Configurations were not merged correctly');
});

it('should return default configuration when custom configuration is empty', () => {
const defaultConfig = { theme: 'light', layout: 'grid' };
const customConfig = {};
const mergedConfig = Widgets.mergeConfigurations(defaultConfig, customConfig);
assert.deepStrictEqual(mergedConfig, defaultConfig, 'Default configuration was not returned correctly');
});
});
});
Loading