From 4b0e16c9e2a0307c466b7a662921ee525d59b50d Mon Sep 17 00:00:00 2001 From: Chessing234 Date: Mon, 1 Jun 2026 13:49:50 +0530 Subject: [PATCH] fix: don't clear list when CSV import column prompt is cancelled When importing a multi-column CSV into a list, the user is asked which column to use via window.prompt. Pressing Cancel returns null, so parseInt(null, 10) is NaN and every row[NaN - 1] is undefined; the subsequent filter drops them all, leaving an empty list value that overwrites the existing contents. Bail out of the import when the prompt is cancelled so the list is left unchanged. Fixes #7313 --- src/containers/monitor.jsx | 4 +- test/unit/containers/monitor.test.jsx | 84 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/unit/containers/monitor.test.jsx diff --git a/src/containers/monitor.jsx b/src/containers/monitor.jsx index f45c2d1ef18..b4acddc3031 100644 --- a/src/containers/monitor.jsx +++ b/src/containers/monitor.jsx @@ -176,7 +176,9 @@ class Monitor extends React.Component { let columnNumber = 1; if (numberOfColumns > 1) { const msg = this.props.intl.formatMessage(messages.columnPrompt, {numberOfColumns}); - columnNumber = parseInt(prompt(msg), 10); // eslint-disable-line no-alert + const response = prompt(msg); // eslint-disable-line no-alert + if (response === null) return; // Cancelled the prompt; leave the list unchanged. + columnNumber = parseInt(response, 10); } const newListValue = rows.map(row => row[columnNumber - 1]) .filter(item => typeof item === 'string'); // CSV importer can leave undefineds diff --git a/test/unit/containers/monitor.test.jsx b/test/unit/containers/monitor.test.jsx new file mode 100644 index 00000000000..a8b720e30ce --- /dev/null +++ b/test/unit/containers/monitor.test.jsx @@ -0,0 +1,84 @@ +import React from 'react'; +import {Provider} from 'react-redux'; +import configureStore from 'redux-mock-store'; +import {mountWithIntl} from '../../helpers/intl-helpers.jsx'; + +import Monitor from '../../../src/containers/monitor'; +import importCSV from '../../../src/lib/import-csv'; +import {setVariableValue} from '../../../src/lib/variable-utils'; + +jest.mock('react-ga'); +jest.mock('../../../src/lib/import-csv', () => ({ + __esModule: true, + default: jest.fn() +})); +jest.mock('../../../src/lib/variable-utils', () => ({ + getVariable: jest.fn(), + setVariableValue: jest.fn() +})); + +const flushPromises = () => new Promise(resolve => setImmediate(resolve)); + +describe('Monitor Container', () => { + const mockStore = configureStore(); + let store; + let vm; + + beforeEach(() => { + vm = { + runtime: { + getLabelForOpcode: jest.fn(() => ({label: 'my list', category: 'data'})), + requestUpdateMonitor: jest.fn() + } + }; + store = mockStore({ + scratchGui: { + monitorLayout: {monitors: {}, savedMonitorPositions: {}}, + theme: {theme: 'default'}, + toolbox: {toolboxXML: ''}, + vm: vm + } + }); + setVariableValue.mockClear(); + }); + + const mountMonitor = () => mountWithIntl( + + + + ); + + test('cancelling the column prompt leaves the list unchanged', async () => { + importCSV.mockReturnValue(Promise.resolve([['a1', 'a2'], ['b1', 'b2']])); + window.prompt = jest.fn(() => null); // user presses cancel + + const wrapper = mountMonitor(); + const instance = wrapper.find('Monitor').instance(); + instance.handleImport(); + await flushPromises(); + + expect(window.prompt).toHaveBeenCalled(); + expect(setVariableValue).not.toHaveBeenCalled(); + }); + + test('choosing a column imports that column into the list', async () => { + importCSV.mockReturnValue(Promise.resolve([['a1', 'a2'], ['b1', 'b2']])); + window.prompt = jest.fn(() => '2'); // user picks the second column + + const wrapper = mountMonitor(); + const instance = wrapper.find('Monitor').instance(); + instance.handleImport(); + await flushPromises(); + + expect(setVariableValue).toHaveBeenCalledWith(vm, 'target1', 'my-list', ['a2', 'b2']); + }); +});