From 112fdf9c1ff030b011ce1200e6f37c5ce8c71fea Mon Sep 17 00:00:00 2001 From: jparez Date: Mon, 13 Jan 2025 10:51:04 +0100 Subject: [PATCH] Reawork ui/ux of iothrottling --- src/plugins/IOThrottling.js | 311 +++++++++++----------- src/plugins/util/components.js | 4 +- src/plugins/util/iothrottling-profiles.js | 12 +- src/scss/base/_animations.scss | 24 ++ src/scss/base/_genymotion.scss | 6 - src/scss/base/_variables.scss | 5 +- src/scss/components/_iothrottling.scss | 111 +++++--- src/scss/components/_widgetwindow.scss | 5 + tests/unit/iothrottling.test.js | 33 ++- 9 files changed, 288 insertions(+), 223 deletions(-) diff --git a/src/plugins/IOThrottling.js b/src/plugins/IOThrottling.js index d145a16d..7a84ef14 100644 --- a/src/plugins/IOThrottling.js +++ b/src/plugins/IOThrottling.js @@ -1,8 +1,10 @@ 'use strict'; const OverlayPlugin = require('./util/OverlayPlugin'); +const {dropdownSelect, textInput, chipTag} = require('./util/components'); const PROFILES = require('./util/iothrottling-profiles'); +const PROFILE_CUSTOM_NAME = 'Custom'; const BYTES_PER_KILOBYTE = 1024; const BYTES_PER_MEGABYTE = BYTES_PER_KILOBYTE << 10; @@ -30,7 +32,25 @@ module.exports = class IOThrottling extends OverlayPlugin { // Register plugin this.instance.diskIO = this; - this.fields = {}; + this.lastReadByteRateReceived = 0; + + // Array of profiles for dropdown + this.profilesForDropdown = PROFILES.map((profile) => { + const divContainer = document.createElement('div'); + const label = document.createElement('div'); + const name = document.createElement('div'); + divContainer.appendChild(name); + divContainer.appendChild(label); + divContainer.style.display = 'flex'; + divContainer.style.justifyContent = 'space-between'; + name.innerHTML = profile.name; + label.innerHTML = profile.label || ''; + return { + element: divContainer, + value: profile.readByteRate ?? profile.name, + valueToDisplay: profile.name || '', + }; + }); // Render components this.renderToolbarButton(); @@ -40,7 +60,8 @@ module.exports = class IOThrottling extends OverlayPlugin { this.instance.registerEventCallback('diskio', (message) => { const values = message.split(' '); if (values[0] === 'readbyterate' && values.length === 2) { - this.updateDiskIOValues(values[1] / BYTES_PER_MEGABYTE); + this.lastReadByteRateReceived = values[1] / BYTES_PER_MEGABYTE; + this.updateDiskIOValues(this.lastReadByteRateReceived); } }); } @@ -69,14 +90,16 @@ module.exports = class IOThrottling extends OverlayPlugin { */ renderWidget() { // Create elements - this.widget = document.createElement('div'); - this.form = document.createElement('form'); - // Generate title - const title = document.createElement('div'); - title.className = 'gm-title'; - title.innerHTML = this.i18n.IOTHROTTLING_TITLE || 'Disk I/O'; - this.form.appendChild(title); + const {modal, container} = this.createTemplateModal({ + title: this.i18n.IOTHROTTLING_TITLE || 'Disk I/O', + classes: 'gm-iothrottling-plugin', + width: 378, + height: 422, + }); + + this.widget = modal; + this.container = container; // Generate input rows const inputs = document.createElement('div'); @@ -85,107 +108,100 @@ module.exports = class IOThrottling extends OverlayPlugin { const IOThrottlingLabel = this.i18n.IOTHROTTLING_PROFILE || 'Profile'; inputs.innerHTML = ''; - // Create select - this.select = document.createElement('select'); - this.select.className = 'gm-iothrottling-select'; - const defaultOption = new Option(this.i18n.IOTHROTTLING_PROFILE_NONE || 'None'); - this.select.add(defaultOption); - this.select.onchange = this.changeProfile.bind(this); - inputs.appendChild(this.select); - - // Add option for each child - PROFILES.forEach((profile) => { - const option = new Option(profile.label, profile.name); - this.select.add(option); + this.dropdownProfile = dropdownSelect.createDropdown({ + items: this.profilesForDropdown, + value: this.profilesForDropdown.find((profile) => profile.value===0).valueToDisplay, + onChange: (newValue) => { + this.updateDiskIOValues(newValue); + }, }); + inputs.appendChild(this.dropdownProfile.element); - this.readByteRateDiv = document.createElement('div'); - this.readByteRateDiv.classList.add('gm-fields'); - const readByteRateLabel = document.createElement('label'); - readByteRateLabel.innerHTML = this.i18n.IOTHROTTLING_READ_BYTERATE || 'Read speed limit:'; - this.readByteRate = document.createElement('input'); - this.readByteRate.className = 'gm-iothrottling-readbyterate'; - this.readByteRate.placeholder = this.i18n.IOTHROTTLING_READ_BYTERATE_EXAMPLE || 'eg: 100'; - this.readByteRate.title = this.i18n.READ_BYTE_RATE || 'Read speed limit'; - this.readByteRate.required = true; - this.readByteRate.pattern = '[0-9]*'; - this.readByteRateDiv.appendChild(readByteRateLabel); - const readByteRateSpeed = document.createElement('label'); - readByteRateSpeed.innerHTML = this.i18n.IOTHROTTLING_BYTERATE_UNIT || 'MiB per sec'; - readByteRateSpeed.classList.add('gm-units'); - this.readByteRateDiv.appendChild(readByteRateSpeed); - this.readByteRateDiv.appendChild(this.readByteRate); - - // Add submit button - this.submitBtn = document.createElement('button'); - this.submitBtn.className = 'gm-iothrottling-update'; - this.submitBtn.innerHTML = this.i18n.IOTHROTTLING_UPDATE || 'Update'; - this.submitBtn.onclick = this.sendDataToInstance.bind(this); + const readByteRateDiv = document.createElement('div'); + readByteRateDiv.classList.add('gm-fields'); + const readByteRateContainer = document.createElement('div'); + readByteRateContainer.classList.add('gm-fields-container'); + readByteRateDiv.appendChild(readByteRateContainer); + + const readByteRateText = document.createElement('div'); + readByteRateText.innerHTML = this.i18n.IOTHROTTLING_READ_BYTERATE || 'Read speed limit:'; + + this.readByteRate = textInput.createTextInput({ + value: '50', + regexFilter: /^[0-9]*$/, + }); + this.readByteRate.element.className = 'gm-iothrottling-readbyterate'; + + const readByteRateSpeedText = document.createElement('div'); + readByteRateSpeedText.innerHTML = this.i18n.IOTHROTTLING_BYTERATE_UNIT || 'MiB per sec'; + readByteRateSpeedText.classList.add('gm-units'); + const readByteRateSpeedNoneText = document.createElement('div'); + readByteRateSpeedNoneText.innerHTML = this.i18n.IOTHROTTLING_BYTERATE_NONE || 'No disk performance alteration'; + readByteRateSpeedNoneText.classList.add('gm-noThrottling'); + const readByteRateSpeedCustomText = document.createElement('div'); + readByteRateSpeedCustomText.innerHTML = + this.i18n.IOTHROTTLING_BYTERATE_CUSTOM || 'Enter the read speed limit you wish to emulate.'; + readByteRateSpeedCustomText.classList.add('gm-customThrottling'); + + readByteRateContainer.appendChild(readByteRateText); + readByteRateContainer.appendChild(this.readByteRate.element); + readByteRateContainer.appendChild(readByteRateSpeedText); + readByteRateContainer.appendChild(readByteRateSpeedNoneText); + readByteRateDiv.appendChild(readByteRateSpeedCustomText); + + // Separator + const separator = document.createElement('div'); + separator.className = 'gm-separator'; + // Add apply button + const applyBtnDiv = document.createElement('div'); + applyBtnDiv.className = 'gm-iothrottling-apply'; + const statusDiv = document.createElement('div'); + statusDiv.className = 'gm-iothrottling-status'; + const statusText = document.createElement('div'); + statusText.innerHTML = 'Status:'; + statusText.className = 'gm-iothrottling-status-text'; + const appliedTag = chipTag.createChip({ + text: 'Applied', + }); + const statusNotApplied = document.createElement('div'); + statusNotApplied.innerHTML = 'Not applied'; + statusNotApplied.className = 'gm-iothrottling-notapplied-text'; + + statusDiv.appendChild(statusText); + statusDiv.appendChild(statusNotApplied); + statusDiv.appendChild(appliedTag.element); + + const applyBtn = document.createElement('button'); + applyBtn.className = 'gm-btn'; + applyBtn.innerHTML = this.i18n.IOTHROTTLING_UPDATE || 'Apply'; + applyBtn.onclick = this.sendDataToInstance.bind(this); + + applyBtnDiv.appendChild(statusDiv); + applyBtnDiv.appendChild(applyBtn); // Add clear cache button + const clearCacheDiv = document.createElement('div'); + clearCacheDiv.className = 'gm-iothrottling-clearcache'; + const clearCacheLabel = this.i18n.CLEAR_CACHE_PROFILE || 'Disk cache'; + clearCacheDiv.innerHTML = ''; + this.clearCacheBtn = document.createElement('button'); - this.clearCacheBtn.className = 'gm-iothrottling-clearcache'; - this.clearCacheBtn.innerHTML = this.i18n.IOTHROTTLING_CLEAR_CACHE || 'Clear cache'; + this.clearCacheBtn.className = 'gm-btn'; + this.clearCacheBtn.innerHTML = this.i18n.IOTHROTTLING_CLEAR_CACHE || 'Clear'; this.clearCacheBtn.onclick = this.clearCache.bind(this); - const clearCacheDiv = document.createElement('div'); clearCacheDiv.appendChild(this.clearCacheBtn); // Setup - this.form.appendChild(inputs); - this.form.appendChild(this.readByteRateDiv); - this.form.appendChild(clearCacheDiv); - this.form.appendChild(this.submitBtn); - - this.setFieldsReadOnly(true); - this.resetFields('0'); - this.displayFields(false); - - this.widget.className = 'gm-overlay gm-iothrottling-plugin gm-hidden'; - - // Add close button - const close = document.createElement('div'); - close.className = 'gm-close-btn'; - close.onclick = this.toggleWidget.bind(this); - - this.widget.appendChild(close); - this.widget.appendChild(this.form); + this.container.appendChild(inputs); + this.container.appendChild(readByteRateDiv); + this.container.appendChild(applyBtnDiv); + this.container.appendChild(separator); + this.container.appendChild(clearCacheDiv); // Render into document this.instance.root.appendChild(this.widget); } - /** - * Set custom fields read-only or not. - * - * @param {boolean} readOnly Desired read-only status. - */ - setFieldsReadOnly(readOnly) { - this.readByteRate.readOnly = readOnly; - } - - /** - * Reset custom fields value. - * - * @param {number} value Desired value. - */ - resetFields(value) { - value = typeof value !== 'undefined' ? value : ''; - this.readByteRate.value = value; - } - - /** - * Toggle custom fields input visibility. - * - * @param {boolean} display Whether or not inputs should be visible. - */ - displayFields(display) { - if (display) { - this.readByteRateDiv.classList.remove('gm-hidden'); - } else { - this.readByteRateDiv.classList.add('gm-hidden'); - } - } - /** * Send information to instance. * @@ -194,14 +210,11 @@ module.exports = class IOThrottling extends OverlayPlugin { sendDataToInstance(event) { event.preventDefault(); - if (this.form.checkValidity()) { - const json = { - channel: 'diskio', - messages: ['set readbyterate ' + this.readByteRate.value * BYTES_PER_MEGABYTE, 'clearcache'], - }; - this.instance.sendEvent(json); - this.toggleWidget(); - } + const json = { + channel: 'diskio', + messages: ['set readbyterate ' + this.readByteRate.getValue() * BYTES_PER_MEGABYTE, 'clearcache'], + }; + this.instance.sendEvent(json); } /** @@ -210,75 +223,61 @@ module.exports = class IOThrottling extends OverlayPlugin { * @param {Event} event Event. */ clearCache(event) { + this.container.classList.remove('gm-iothrottling-cache-cleared'); + // Force a reflow to restart the animation + void this.container.offsetWidth; + this.container.classList.add('gm-iothrottling-cache-cleared'); event.preventDefault(); const json = {channel: 'diskio', messages: ['clearcache']}; this.instance.sendEvent(json); } - /** - * Handles profile change. - */ - changeProfile() { - const profile = PROFILES.find((elem) => elem.name === this.select.value); - if (profile && profile.name !== 'Custom') { - this.loadDetails(profile); - this.displayFields(true); - } else if (profile && profile.name === 'Custom') { - this.setFieldsReadOnly(false); - this.displayFields(true); - } else { - this.resetFields('0'); - this.displayFields(false); - } - } - /** * Handles disk I/O parameters changes. Keeps UI in sync with the instance state. * * @param {number} readSpeed Read byte rate. */ updateDiskIOValues(readSpeed) { - readSpeed = Number(readSpeed); + this.container.classList.remove('gm-iothrottling-saved'); + // Handle the chipTag display. If readSpeed is custom (trigger from dropdown) + const readSpeedIsCustom = readSpeed === PROFILE_CUSTOM_NAME; + if (readSpeedIsCustom) { + // if the lastReadByteRateReceived exists in the profiles the active profil isn't a custom one + const profile = this.profilesForDropdown.find((p) => p.value === this.lastReadByteRateReceived); + + if (!profile) { + this.container.classList.add('gm-iothrottling-saved'); + } + } else if (readSpeed === this.lastReadByteRateReceived) { + // if readSpeed isn't custom and is equal to the lastReadByteRateReceived then the active profil is dropdown profil + this.container.classList.add('gm-iothrottling-saved'); + } - if (Number.isNaN(readSpeed) || readSpeed <= 0) { - this.select.value = this.i18n.IOTHROTTLING_PROFILE_NONE || 'None'; - this.resetFields('0'); - this.displayFields(false); + // if readspeed is not a number or is less than 0 then set select "none" profile + if (!readSpeedIsCustom && (Number.isNaN(readSpeed) || readSpeed <= 0)) { + this.readByteRate.setValue(0); + this.dropdownProfile.setValue(this.profilesForDropdown.find((profile) => profile.value===0)); + // Display Read speed limit: No disk performance alteration + this.container.classList.add('gm-iothrottling-none'); return; } - this.readByteRate.value = readSpeed; - const profile = PROFILES.find((elem) => elem.readByteRate === readSpeed); - if (profile && profile.name !== 'Custom') { - this.select.value = profile.name; - this.loadDetails(profile); - this.displayFields(true); - } else { - this.select.value = 'Custom'; - this.setFieldsReadOnly(false); - this.displayFields(true); - } - } + this.container.classList.remove('gm-iothrottling-none'); + this.container.classList.remove('gm-iothrottling-custom'); + const profile = this.profilesForDropdown.find((prof) => prof.value === readSpeed); - /** - * Load fields with the given profile info. - * - * @param {Object} profile Selected profile. - */ - loadDetails(profile) { - if (profile.name !== 'Custom') { - this.setFieldsReadOnly(true); - this.readByteRate.value = profile.readByteRate; + if (!readSpeedIsCustom && profile) { + this.readByteRate.setReadOnly(true); + this.dropdownProfile.setValue(profile); + this.readByteRate.setValue(readSpeed); + } else { + // custom + this.container.classList.add('gm-iothrottling-custom'); + this.readByteRate.setReadOnly(false); + const custom = this.profilesForDropdown.find((prof) => prof.value === PROFILE_CUSTOM_NAME); + this.dropdownProfile.setValue(custom); + this.readByteRate.setValue(this.lastReadByteRateReceived); } } - - /** - * Activate disk I/O throttling. Keeps UI in sync with the instance state. - * - * @param {number} readSpeed Read byte rate. - */ - setActive(readSpeed) { - this.updateDiskIOValues(Number(readSpeed) / BYTES_PER_KILOBYTE); - } }; diff --git a/src/plugins/util/components.js b/src/plugins/util/components.js index 969beb9f..a4c6032a 100644 --- a/src/plugins/util/components.js +++ b/src/plugins/util/components.js @@ -256,14 +256,14 @@ const dropdownSelect = (() => { * @returns {Object} - Dropdown component with methods to interact with it. */ const createDropdown = ({items = [], onChange = null, value = ''}) => { - let selectedValue = value || 'Select...'; + let selectedValue = value ?? 'Select...'; const dropdownDiv = document.createElement('div'); dropdownDiv.className = 'dropdown'; // Create the div displaying the selected value const selectedValueDiv = document.createElement('div'); selectedValueDiv.className = 'dropdown-selected'; - selectedValueDiv.textContent = value || 'Select...'; + selectedValueDiv.textContent = value ?? 'Select...'; dropdownDiv.appendChild(selectedValueDiv); // Create the menu div where options will be appended diff --git a/src/plugins/util/iothrottling-profiles.js b/src/plugins/util/iothrottling-profiles.js index 809b00f7..83b40ca2 100644 --- a/src/plugins/util/iothrottling-profiles.js +++ b/src/plugins/util/iothrottling-profiles.js @@ -10,23 +10,27 @@ * */ module.exports = [ + { + name: 'None', + label: '(No disk performance alteration)', + readByteRate: 0, + }, { name: 'High-end device', - label: 'High-end device', + label: '(200 MiB per second)', readByteRate: 200, }, { name: 'Mid-range device', - label: 'Mid-range device', + label: '(100 MiB per second)', readByteRate: 100, }, { name: 'Low-end device', - label: 'Low-end device', + label: '(50 MiB per second)', readByteRate: 50, }, { name: 'Custom', - label: 'Custom device', }, ]; diff --git a/src/scss/base/_animations.scss b/src/scss/base/_animations.scss index 70bc1374..589bdfda 100644 --- a/src/scss/base/_animations.scss +++ b/src/scss/base/_animations.scss @@ -9,3 +9,27 @@ opacity: 1; } } + +@keyframes blink-alert { + 0% { + opacity: 1; + } + 10% { + opacity: 0.3; + } + 20% { + opacity: 1; + } + 30% { + opacity: 0.3; + } + 40% { + opacity: 1; + } + 50% { + opacity: 0.3; + } + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/src/scss/base/_genymotion.scss b/src/scss/base/_genymotion.scss index 5bb38d9f..fb975f63 100644 --- a/src/scss/base/_genymotion.scss +++ b/src/scss/base/_genymotion.scss @@ -210,12 +210,6 @@ } } -.gm-modal { - .gm-separator { - - } -} - .gm-separator { width: 100%; height: 1px; diff --git a/src/scss/base/_variables.scss b/src/scss/base/_variables.scss index 8201e906..c4c56f34 100644 --- a/src/scss/base/_variables.scss +++ b/src/scss/base/_variables.scss @@ -46,10 +46,13 @@ // Responsive breakpoint $breakpoint-mobile: 650px; -//Spacing +// Spacing $spacing-xxs: 2px; $spacing-xs: 5px; $spacing-s: 10px; $spacing-m: 15px; $spacing-l: 20px; $spacing-xl: 30px; + +// Modal +$modal-x-padding: 40px; \ No newline at end of file diff --git a/src/scss/components/_iothrottling.scss b/src/scss/components/_iothrottling.scss index e52236ce..05e3e87c 100644 --- a/src/scss/components/_iothrottling.scss +++ b/src/scss/components/_iothrottling.scss @@ -2,57 +2,94 @@ * IO Throttling plugin styles */ .device-renderer-instance .gm-iothrottling-plugin { - .gm-inputs { - margin-bottom: 15px; - select { - font-size: medium; - width: 100%; - margin-top: 10px; - padding: 2px; + .gm-noThrottling, + .gm-customThrottling { + display: none; + } + .gm-iothrottling-none { + .gm-units, + .gm-iothrottling-readbyterate { + display: none; + } + .gm-noThrottling { + display: block; + } + } + .gm-iothrottling-custom { + .gm-customThrottling { + display: block; + //text italic + font-style: italic; + margin-top: 15px; } } - label { - margin-top: 20px; - display: block; - float: left; + .gm-iothrottling-cache-cleared{ + .gm-iothrottling-clearcache{ + label { + animation: blink-alert 1.5s ease-in-out + } + } } - .gm-fields { + .gm-tag-success { + display: none; + } + .gm-iothrottling-notapplied-text { display: block; - margin: 0; - padding: 0; - width: 100%; - height: 50px; } - - .gm-units { - text-align: right; - float: right; - width: 160px; + .gm-iothrottling-saved { + .gm-tag-success { + display: block; + } + .gm-iothrottling-notapplied-text { + display: none; + } } - input { - font-size: medium; - font-family: Helvetica, sans-serif; - margin-top: 10px; - width: 30%; - text-align: right; - float: right; + .gm-modal-body { + display: flex; + flex-direction: column; + } - &:read-only { - border-bottom: 1px solid transparent; - background: var(--gm-btn-bg-color-disabled); - } + .gm-fields { + margin: 15px 0; + min-height: 40px; + flex: 1; + font-size: 13px; - &:required:invalid, - &:focus:invalid { - border-bottom: 1px solid var(--gm-btn-bg-color); + .gm-fields-container{ + display: flex; + align-items: center; + gap: 10px; + .gm-iothrottling-readbyterate{ + width: 49px; + input{ + text-align: center; + } + } + .gm-units{ + margin-left: 20px; + } } } - button { - margin-top: 20px; + .gm-iothrottling-clearcache, + .gm-iothrottling-apply{ + display: flex; + justify-content: space-between; + align-items: center; + height: 60px; + .gm-iothrottling-status{ + display: flex; + align-items: center; + gap: 5px; + .gm-iothrottling-notapplied-text{ + font-style: italic; + color: #7f7f7f // TODO + + } + } } } diff --git a/src/scss/components/_widgetwindow.scss b/src/scss/components/_widgetwindow.scss index 50c4acde..095757a0 100644 --- a/src/scss/components/_widgetwindow.scss +++ b/src/scss/components/_widgetwindow.scss @@ -217,4 +217,9 @@ display: flex; flex-direction: column; } + + .gm-separator { + width: calc(100% + $modal-x-padding + $modal-x-padding); + margin-left: -$modal-x-padding; + } } diff --git a/tests/unit/iothrottling.test.js b/tests/unit/iothrottling.test.js index 76aa43e2..572651e0 100644 --- a/tests/unit/iothrottling.test.js +++ b/tests/unit/iothrottling.test.js @@ -26,10 +26,11 @@ describe('IOThrottling Plugin', () => { instance = new Instance(); new IOThrottling(instance, { IOTHROTTLING_TITLE: 'TEST IOTHROTTLING PLUGIN TITLE', - IOTHROTTLING_PROFILE: 'TEST IOTHROTTLING PLUGIN PROFILE', IOTHROTTLING_PROFILE_NONE: 'TEST IOTHROTTLING PLUGIN PROFILE NONE', + IOTHROTTLING_PROFILE: 'TEST PROFILE', IOTHROTTLING_READ_BYTERATE: 'TEST IOTHROTTLING PLUGIN READ BYTERATE', - IOTHROTTLING_READ_BYTERATE_EXAMPLE: 'TEST IOTHROTTLING PLUGIN READ BYTERATE EXAMPLE', + IOTHROTTLING_READ_BYTERATE_CUSTOM: 'TEST IOTHROTTLING PLUGIN READ BYTERATE CUSTOM', + IOTHROTTLING_READ_BYTERATE_NONE: 'TEST IOTHROTTLING PLUGIN READ BYTERATE NONE', IOTHROTTLING_BYTERATE_UNIT: 'TEST IOTHROTTLING PLUGIN BYTERATE UNIT', IOTHROTTLING_UPDATE: 'TEST IOTHROTTLING PLUGIN UPDATE BUTTON', IOTHROTTLING_CLEAR_CACHE: 'TEST IOTHROTTLING PLUGIN CLEAR CACHE BUTTON', @@ -46,10 +47,8 @@ describe('IOThrottling Plugin', () => { test('has translations', () => { expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN TITLE')); - expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN PROFILE')); - expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN PROFILE NONE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PROFILE')); expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN READ BYTERATE')); - expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN READ BYTERATE EXAMPLE')); expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN BYTERATE UNIT')); expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN UPDATE BUTTON')); expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN CLEAR CACHE BUTTON')); @@ -60,13 +59,13 @@ describe('IOThrottling Plugin', () => { test('diskio', () => { ['jean-michel', 'readbyterate -123', '', 'readbyterate invalid'].forEach((invalidValue) => { instance.emit('diskio', invalidValue); - expect(diskio.select.value).toBe('None'); + expect(diskio.dropdownProfile.getValue()).toBe('None'); }); [69, 420].forEach((customValue) => { instance.emit('diskio', `readbyterate ${customValue * 1024 * 1024}`); - expect(diskio.select.value).toBe('Custom'); - expect(Number(diskio.readByteRate.value)).toBe(customValue); + expect(diskio.dropdownProfile.getValue()).toBe('Custom'); + expect(Number(diskio.readByteRate.getValue())).toBe(customValue); }); IOThrottlingProfiles.forEach((profile) => { @@ -75,8 +74,8 @@ describe('IOThrottling Plugin', () => { } instance.emit('diskio', `readbyterate ${profile.readByteRate * 1024 * 1024}`); - expect(diskio.select.value).toBe(profile.name); - expect(Number(diskio.readByteRate.value)).toBe(profile.readByteRate); + expect(diskio.dropdownProfile.getValue()).toBe(profile.readByteRate); + expect(Number(diskio.readByteRate.getValue())).toBe(profile.readByteRate); }); }); }); @@ -92,9 +91,9 @@ describe('IOThrottling Plugin', () => { sendEventSpy.mockClear(); instance.outgoingMessages = []; - diskio.select.value = profile.name; - diskio.select.dispatchEvent(new Event('change')); - diskio.submitBtn.click(); + const dropDownProfile = diskio.profilesForDropdown.find((p) => p.value === profile.readByteRate); + diskio.dropdownProfile.setValue(dropDownProfile, true); + diskio.widget.querySelector('.gm-btn').click(); expect(sendEventSpy).toHaveBeenCalledTimes(1); expect(instance.outgoingMessages[0]).toEqual({ channel: 'diskio', @@ -104,10 +103,10 @@ describe('IOThrottling Plugin', () => { sendEventSpy.mockClear(); instance.outgoingMessages = []; - diskio.select.value = 'Custom'; - diskio.select.dispatchEvent(new Event('change')); - diskio.readByteRate.value = 69; // Nice - diskio.submitBtn.click(); + const dropDownProfile = diskio.profilesForDropdown.find((p) => p.name === 'Custom'); + diskio.dropdownProfile.setValue(dropDownProfile, true); + diskio.readByteRate.setValue(69); + diskio.widget.querySelector('.gm-btn').click(); expect(sendEventSpy).toHaveBeenCalledTimes(1); expect(instance.outgoingMessages[0]).toEqual({ channel: 'diskio',