Index: src/sas/qtgui/GUITests.py
===================================================================
--- src/sas/qtgui/GUITests.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
+++ src/sas/qtgui/GUITests.py	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
@@ -48,4 +48,5 @@
 from Utilities.UnitTesting import PluginDefinitionTest
 from Utilities.UnitTesting import TabbedModelEditorTest
+from Utilities.UnitTesting import AddMultEditorTest
 
 # Unit Testing
@@ -107,4 +108,5 @@
         unittest.makeSuite(PluginDefinitionTest.PluginDefinitionTest,  'test'),
         unittest.makeSuite(TabbedModelEditorTest.TabbedModelEditorTest,'test'),
+        unittest.makeSuite(AddMultEditorTest.AddMultEditorTest, 'test'),
 
         # Calculators
Index: src/sas/qtgui/MainWindow/GuiManager.py
===================================================================
--- src/sas/qtgui/MainWindow/GuiManager.py	(revision 8ac35512dd80b475772933cefe78a3437a8ec204)
+++ src/sas/qtgui/MainWindow/GuiManager.py	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
@@ -39,4 +39,6 @@
 from sas.qtgui.Perspectives.Fitting.FittingPerspective import FittingWindow
 from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow, DEFAULT_PERSPECTIVE
+
+from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
 
 class Acknowledgements(QDialog, Ui_Acknowledgements):
@@ -407,4 +409,5 @@
         self._workspace.actionEdit_Custom_Model.triggered.connect(self.actionEdit_Custom_Model)
         self._workspace.actionManage_Custom_Models.triggered.connect(self.actionManage_Custom_Models)
+        self._workspace.actionAddMult_Models.triggered.connect(self.actionAddMult_Models)
         # Window
         self._workspace.actionCascade.triggered.connect(self.actionCascade)
@@ -681,4 +684,13 @@
         self.model_manager.show()
 
+    def actionAddMult_Models(self):
+        """
+        """
+        print("Open simple add / multiply editor")
+        # Add Simple Add / Multiple Editor
+        self.add_mult_editor = AddMultEditor(self)
+        self.add_mult_editor.show()
+        # pass
+
     #============ ANALYSIS =================
     def actionFitting(self):
Index: src/sas/qtgui/MainWindow/UI/MainWindowUI.ui
===================================================================
--- src/sas/qtgui/MainWindow/UI/MainWindowUI.ui	(revision 8ac35512dd80b475772933cefe78a3437a8ec204)
+++ src/sas/qtgui/MainWindow/UI/MainWindowUI.ui	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
@@ -25,5 +25,5 @@
      <y>0</y>
      <width>915</width>
-     <height>26</height>
+     <height>22</height>
     </rect>
    </property>
@@ -111,4 +111,5 @@
     <addaction name="actionEdit_Custom_Model"/>
     <addaction name="actionManage_Custom_Models"/>
+    <addaction name="actionAddMult_Models"/>
    </widget>
    <widget class="QMenu" name="menuWindow">
@@ -526,4 +527,9 @@
    </property>
   </action>
+  <action name="actionAddMult_Models">
+   <property name="text">
+    <string>Add/Multiply Models</string>
+   </property>
+  </action>
  </widget>
  <resources/>
Index: src/sas/qtgui/Utilities/AddMultEditor.py
===================================================================
--- src/sas/qtgui/Utilities/AddMultEditor.py	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
+++ src/sas/qtgui/Utilities/AddMultEditor.py	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
@@ -0,0 +1,268 @@
+"""
+Widget for simple add / multiply editor.
+"""
+# numpy methods required for the validator! Don't remove.
+# pylint: disable=unused-import,unused-wildcard-import,redefined-builtin
+from numpy import *
+
+from PyQt5 import QtCore
+from PyQt5 import QtGui
+from PyQt5 import QtWidgets
+import webbrowser
+
+import os
+import numpy as np
+import re
+import logging
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from sasmodels.sasview_model import load_standard_models
+from sas.qtgui.Perspectives.Fitting import ModelUtilities
+
+# Local UI
+from sas.qtgui.Utilities.UI.AddMultEditorUI import Ui_AddMultEditorUI
+
+# Template for the output plugin file
+SUM_TEMPLATE = """
+from sasmodels.core import load_model_info
+from sasmodels.sasview_model import make_model_from_info
+
+model_info = load_model_info('{model1}{operator}{model2}')
+model_info.name = '{name}'
+model_info.description = '{desc_line}'
+Model = make_model_from_info(model_info)
+"""
+
+# Color of backgrounds to underline valid or invalid input
+BG_WHITE = "background-color: rgb(255, 255, 255);"
+BG_RED = "background-color: rgb(244, 170, 164);"
+
+
+class AddMultEditor(QtWidgets.QDialog, Ui_AddMultEditorUI):
+    """
+       Dialog for easy custom composite models.  Provides a Dialog panel
+       to choose two existing models (including pre-existing Plugin Models which
+       may themselves be composite models) as well as an operation on those models
+       (add or multiply) the resulting model will add a scale parameter and a
+       background parameter.
+       The user also gives a brief help for the model in a description box and
+       must provide a unique name which is verified as unique before the new
+    """
+    def __init__(self, parent=None):
+        super(AddMultEditor, self).__init__()
+
+        self.parent = parent
+
+        self.setupUi(self)
+
+        #  uncheck self.chkOverwrite
+        self.chkOverwrite.setChecked(False)
+
+        # Disabled Apply button until input of valid output plugin name
+        self.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).setEnabled(False)
+
+        # Flag for correctness of resulting name
+        self.good_name = False
+
+        self.setupSignals()
+
+        self.list_models = self.readModels()
+
+        # Fill models' comboboxes
+        self.setupModels()
+
+        self.setFixedSize(self.minimumSizeHint())
+
+        # Name and directory for saving new plugin model
+        self.plugin_filename = None
+        self.plugin_dir = ModelUtilities.find_plugins_dir()
+
+        # Validators
+        rx = QtCore.QRegExp("^[A-Za-z0-9_]*$")
+        txt_validator = QtGui.QRegExpValidator(rx)
+        self.txtName.setValidator(txt_validator)
+
+    def setupModels(self):
+        """ Add list of models to 'Model1' and 'Model2' comboboxes """
+        # Load the model dict
+        self.cbModel1.addItems(self.list_models)
+        self.cbModel2.addItems(self.list_models)
+
+        # set the default initial value of Model1 and Model2
+        index_ini_model1 = self.cbModel1.findText('sphere', QtCore.Qt.MatchFixedString)
+
+        if index_ini_model1 >= 0:
+            self.cbModel1.setCurrentIndex(index_ini_model1)
+        else:
+            self.cbModel1.setCurrentIndex(0)
+
+        index_ini_model2 = self.cbModel2.findText('cylinder',
+                                                  QtCore.Qt.MatchFixedString)
+        if index_ini_model2 >= 0:
+            self.cbModel2.setCurrentIndex(index_ini_model2)
+        else:
+            self.cbModel2.setCurrentIndex(0)
+
+    def readModels(self):
+        """ Generate list of models """
+        models = load_standard_models()
+        models_dict = {}
+        for model in models:
+            models_dict[model.name] = model
+
+        return sorted([model_name for model_name in models_dict])
+
+    def setupSignals(self):
+        """ Signals from various elements """
+        # check existence of output filename when entering name
+        # or when overwriting not allowed
+        self.txtName.editingFinished.connect(self.onNameCheck)
+        self.chkOverwrite.stateChanged.connect(self.onNameCheck)
+
+        self.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.onApply)
+        self.buttonBox.button(QtWidgets.QDialogButtonBox.Help).clicked.connect(self.onHelp)
+        self.buttonBox.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self.close)
+
+        # change displayed equation when changing operator
+        self.cbOperator.currentIndexChanged.connect(self.onOperatorChange)
+
+    def onNameCheck(self):
+        """
+        Check if proposed new model name does not already exists
+        (if the overwriting is not allowed).
+        If not an error message not show error message is displayed
+        """
+
+        # Get the function/file name
+        title = self.txtName.text().lstrip().rstrip()
+
+        filename = title + '.py'
+
+        if self.chkOverwrite.isChecked():
+            # allow overwriting -> only valid name needs to be checked
+            # (done with validator in __init__ above)
+            self.good_name = True
+            self.txtName.setStyleSheet(BG_WHITE)
+            self.plugin_filename = os.path.join(self.plugin_dir, filename)
+        else:
+            # No overwriting -> check existence of filename
+            # Create list of existing model names for comparison
+            # fake existing regular model name list
+            models_list = [item + '.py' for item in self.list_models]
+            if filename in models_list:
+                self.good_name = False
+                self.txtName.setStyleSheet(BG_RED)
+                msg = "Plugin with specified name already exists.\n"
+                msg += "Please specify different filename or allow file overwrite."
+                logging.warning(msg)
+                QtWidgets.QMessageBox.critical(self, 'Plugin Error', msg)
+            else:
+                s_title = title
+                if len(title) > 20:
+                    s_title = title[0:19] + '...'
+                logging.info("Model function ({}) has been set!\n".
+                                format(str(s_title)))
+                self.good_name = True
+                self.txtName.setStyleSheet(BG_WHITE)
+                self.plugin_filename = os.path.join(self.plugin_dir, filename)
+
+        # Enable Apply push button only if valid name
+        self.buttonBox.button(
+            QtWidgets.QDialogButtonBox.Apply).setEnabled(self.good_name)
+
+        return self.good_name
+
+    def onOperatorChange(self, index):
+        """ Respond to operator combo box changes """
+
+        self.lblEquation.setText("Plugin_model = scale_factor * "
+                                 "(model_1 {} model_2) + background".
+                                 format(self.cbOperator.currentText()))
+
+    def onApply(self):
+        """ Validity check, save model to file """
+
+        # if name OK write file and test
+        self.buttonBox.button(
+            QtWidgets.QDialogButtonBox.Apply).setEnabled(False)
+
+        self.write_new_model_to_file(self.plugin_filename,
+                                     self.cbModel1.currentText(),
+                                     self.cbModel2.currentText(),
+                                     self.cbOperator.currentText())
+
+        success = self.checkModel(self.plugin_filename)
+
+        if success:
+            # Update list of models in FittingWidget and AddMultEditor
+            self.parent.communicate.customModelDirectoryChanged.emit()
+            self.updateModels()
+
+    def write_new_model_to_file(self, fname, model1_name, model2_name, operator):
+        """ Write and Save file """
+
+        description = self.txtDescription.text().lstrip().rstrip()
+        if description.strip() != '':
+            # Sasmodels generates a description for us. If the user provides
+            # their own description, add a line to overwrite the sasmodels one
+            desc_line = description
+        else:
+            desc_line = "{} {} {}".format(model1_name,
+                                          operator,
+                                          model2_name)
+
+        name = os.path.splitext(os.path.basename(fname))[0]
+        output = SUM_TEMPLATE.format(name=name,
+                                     model1=model1_name,
+                                     model2=model2_name,
+                                     operator=operator,
+                                     desc_line=desc_line)
+
+        with open(fname, 'w') as out_f:
+            out_f.write(output)
+
+    # same version as in TabbedModelEditor
+    @classmethod
+    def checkModel(cls, path):
+        """ Check that the model saved in file 'path' can run. """
+
+        # try running the model
+        from sasmodels.sasview_model import load_custom_model
+        Model = load_custom_model(path)
+        model = Model()
+        q = np.array([0.01, 0.1])
+        _ = model.evalDistribution(q)
+        qx, qy = np.array([0.01, 0.01]), np.array([0.1, 0.1])
+        _ = model.evalDistribution([qx, qy])
+        # check the model's unit tests run
+        from sasmodels.model_test import run_one
+        # TODO see comments in TabbedModelEditor
+        # result = run_one(path)
+        result = True  # to be commented out
+        return result
+
+    def updateModels(self):
+        """ Update contents of comboboxes with new plugin models """
+
+        self.cbModel1.blockSignals(True)
+        self.cbModel1.clear()
+        self.cbModel1.blockSignals(False)
+
+        self.cbModel2.blockSignals(True)
+        self.cbModel2.clear()
+        self.cbModel2.blockSignals(False)
+        # Retrieve the list of models
+        model_list = self.readModels()
+        # Populate the models comboboxes
+        self.cbModel1.addItems(model_list)
+        self.cbModel2.addItems(model_list)
+
+    def onHelp(self):
+        """ Display related help section """
+
+        try:
+            help_location = GuiUtils.HELP_DIRECTORY_LOCATION + \
+                            "/user/sasgui/perspectives/fitting/fitting_help.html#sum-multi-p1-p2"
+            webbrowser.open('file://' + os.path.realpath(help_location))
+        except AttributeError:
+            # No manager defined - testing and standalone runs
+            pass
Index: src/sas/qtgui/Utilities/UI/AddMultEditorUI.ui
===================================================================
--- src/sas/qtgui/Utilities/UI/AddMultEditorUI.ui	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
+++ src/sas/qtgui/Utilities/UI/AddMultEditorUI.ui	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddMultEditorUI</class>
+ <widget class="QDialog" name="AddMultEditorUI">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>527</width>
+    <height>331</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>527</width>
+    <height>331</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Easy Add/Multiply Editor</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="1" column="0">
+    <widget class="QGroupBox" name="groupBox_5">
+     <property name="title">
+      <string>Description</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLineEdit" name="txtDescription">
+        <property name="placeholderText">
+         <string>Enter a description of the model (optional)</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Plugin model</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_5">
+      <item row="0" column="0">
+       <widget class="QLineEdit" name="txtName">
+        <property name="toolTip">
+         <string>Sum / Multiply model function name.</string>
+        </property>
+        <property name="placeholderText">
+         <string>Enter a plugin name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="chkOverwrite">
+        <property name="toolTip">
+         <string>Check to overwrite the existing model with the same name.</string>
+        </property>
+        <property name="text">
+         <string>Overwrite existing model</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Model selection</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <layout class="QGridLayout" name="gridLayout">
+        <item row="0" column="0">
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>model_1</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="2">
+         <widget class="QLabel" name="label_2">
+          <property name="text">
+           <string>model_2</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QComboBox" name="cbModel1">
+          <property name="editable">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QComboBox" name="cbOperator">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="toolTip">
+           <string>Add: +
+Multiply: *</string>
+          </property>
+          <item>
+           <property name="text">
+            <string>+</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>*</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="1" column="2">
+         <widget class="QComboBox" name="cbModel2">
+          <property name="editable">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="lblEquation">
+     <property name="text">
+      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Plugin_model = scale_factor * (model_1 + model_2) + background&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Index: src/sas/qtgui/Utilities/UnitTesting/AddMultEditorTest.py
===================================================================
--- src/sas/qtgui/Utilities/UnitTesting/AddMultEditorTest.py	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
+++ src/sas/qtgui/Utilities/UnitTesting/AddMultEditorTest.py	(revision 01ef3f797e8d4032a84fb98c0186de9326036df9)
@@ -0,0 +1,342 @@
+import sys
+import os
+import unittest
+import webbrowser
+import tempfile
+from unittest.mock import MagicMock, patch
+
+from PyQt5 import QtGui
+from PyQt5 import QtWidgets
+
+# set up import paths
+import path_prepare
+
+from sas.qtgui.Utilities.GuiUtils import Communicate
+
+# Local
+from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
+
+if not QtWidgets.QApplication.instance():
+    app = QtWidgets.QApplication(sys.argv)
+
+class dummy_manager(object):
+    HELP_DIRECTORY_LOCATION = "html"
+    communicate = Communicate()
+
+class AddMultEditorTest(unittest.TestCase):
+    """ Test the simple AddMultEditor dialog """
+    @patch.object(AddMultEditor, 'readModels')
+    def setUp(self, mock_list_models):
+        """ Create AddMultEditor dialog """
+
+        # mock models from plugin folder
+        mock_list_models.return_value = ['cylinder', 'rpa',
+                                         'core_shell_cylinder', 'sphere']
+
+        self.widget = AddMultEditor(dummy_manager())
+
+    def tearDown(self):
+        """ Destroy the GUI """
+
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        """ Test the GUI in its default state """
+
+        self.assertIsInstance(self.widget, QtWidgets.QDialog)
+
+        self.assertEqual(self.widget.sizePolicy().verticalPolicy(), 0)
+        self.assertEqual(self.widget.sizePolicy().horizontalPolicy(), 5)
+
+        # Default title
+        self.assertEqual(self.widget.windowTitle(), "Easy Add/Multiply Editor")
+
+        # Default types
+        self.assertIsInstance(self.widget.cbOperator, QtWidgets.QComboBox)
+        self.assertIsInstance(self.widget.cbModel1, QtWidgets.QComboBox)
+        self.assertIsInstance(self.widget.cbModel2, QtWidgets.QComboBox)
+
+        # Modal window
+        self.assertFalse(self.widget.isModal())
+
+        # Checkbox not tristate, not checked
+        self.assertFalse(self.widget.chkOverwrite.isTristate())
+        self.assertFalse(self.widget.chkOverwrite.isChecked())
+
+        # Push buttons disabled
+        self.assertFalse(self.widget.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).isEnabled())
+
+        # Text of labels...
+        self.assertEqual(self.widget.chkOverwrite.text(),
+                         "Overwrite existing model")
+
+        # Allowed operators
+        self.assertListEqual([str(self.widget.cbOperator.itemText(i)) for i in
+                              range(self.widget.cbOperator.count())],
+                             ['+', '*'])
+
+        # Default operator
+        self.assertEqual(self.widget.cbOperator.currentText(), '+')
+
+        # checkbox unchecked
+        self.assertFalse(self.widget.chkOverwrite.isChecked())
+
+        # Default 'good_name' flag, name for plugin file and plugin dir
+        self.assertFalse(self.widget.good_name)
+        self.assertIsNone(self.widget.plugin_filename)
+
+        # default content of displayed equation (to create the new model)
+        self.assertEqual(self.widget.lblEquation.text(),
+                         "<html><head/><body><p>Plugin_model = "
+                         "scale_factor * (model_1 + model_2) + background"
+                         "</p></body></html>")
+
+        # Tooltips
+        self.assertEqual(self.widget.cbOperator.toolTip(),
+                         "Add: +\nMultiply: *")
+        self.assertEqual(self.widget.txtName.toolTip(),
+                         "Sum / Multiply model function name.")
+        self.widget.chkOverwrite.setChecked(True)
+
+        self.assertNotEqual(len(self.widget.chkOverwrite.toolTip()), 0)
+
+    def testOnModelComboboxes(self):
+        """ Test on Model1 and Model2 comboboxes """
+
+        # content of model_1 and model_2 comboboxes
+        # same content for the two comboboxes
+        self.assertEqual(self.widget.cbModel1.count(),
+                         self.widget.cbModel2.count())
+
+        self.assertEqual(self.widget.cbModel1.count(), 4)
+
+        # default of cbModel1 and cbModel2
+        self.assertEqual(self.widget.cbModel1.currentText(), 'sphere')
+        self.assertEqual(self.widget.cbModel2.currentText(), 'cylinder')
+
+    def testValidateName(self):
+        """ Test validity of output name (syntax only) """
+
+        # Invalid plugin name
+        self.widget.txtName.setText('+++')
+
+        state = self.widget.txtName.validator().validate(self.widget.txtName.text(), 0)[0]
+        self.assertEqual(state, QtGui.QValidator.Invalid)
+
+        # Valid new plugin name
+        self.widget.txtName.setText('cylinder_test_case')
+        state = \
+        self.widget.txtName.validator().validate(self.widget.txtName.text(),
+                                                 0)[0]
+        self.assertEqual(state, QtGui.QValidator.Acceptable)
+
+    def testOnApply(self):
+        """ Test onApply """
+
+        self.widget.txtName.setText("new_model")
+        self.widget.updateModels = MagicMock()
+
+        # make sure the flag is set correctly
+        self.widget.is_modified = True
+
+        # Mock self.write_new_model_to_file
+        self.widget.plugin_dir = tempfile.gettempdir()
+        self.widget.plugin_filename = \
+            os.path.join(self.widget.plugin_dir, 'new_model.py')
+
+        self.widget.write_new_mode_to_file = MagicMock()
+
+        # invoke the tested method
+        self.widget.onApply()
+
+        self.assertTrue(self.widget.write_new_mode_to_file.called_once_with(
+        os.path.join(self.widget.plugin_dir, 'new_model.py'),
+        self.widget.cbModel1.currentText(),
+        self.widget.cbModel2.currentText(),
+        self.widget.cbOperator.currentText()))
+
+        self.assertFalse(self.widget.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).isEnabled())
+
+        self.assertTrue(self.widget.checkModel(self.widget.plugin_filename))
+
+        self.assertTrue(self.widget.updateModels.called_once())
+
+    def testWriteNewModelToFile(self):
+        """ Test content of generated plugin"""
+
+        dummy_file_path = os.path.join(tempfile.gettempdir(),
+                                       "test_datafile.py")
+
+        # prepare data to create file and check its content: names of 2 models,
+        # operator and description (default or not)
+        model1_name = self.widget.cbModel1.currentText()
+        model2_name = self.widget.cbModel2.currentText()
+        symbol_operator = self.widget.cbOperator.currentText()
+
+        # default description
+        desc_line = "{} {} {}".format(model1_name,
+                                      symbol_operator,
+                                      model2_name)
+
+        self.widget.write_new_model_to_file(dummy_file_path, model1_name,
+                                            model2_name, symbol_operator)
+        # check content of dummy file path
+        with open(dummy_file_path, 'r') as f_in:
+            lines_from_generated_file = [line.strip() for line in f_in]
+
+        # SUM_TEMPLATE with updated entries
+        completed_template = ["from sasmodels.core import load_model_info",
+                        "from sasmodels.sasview_model import make_model_from_info",
+                        "model_info = load_model_info('{model1}{operator}{model2}')".
+                            format(model1=model1_name,
+                                   operator=symbol_operator,
+                                   model2=model2_name),
+                        "model_info.name = '{}'".format("test_datafile"),
+                        "model_info.description = '{}'".format(desc_line),
+                        "Model = make_model_from_info(model_info)"]
+
+        for item in completed_template:
+            self.assertIn(item, lines_from_generated_file)
+
+        # check content with description entered by user
+        self.widget.txtDescription.setText('New description for test  ')
+
+        new_desc_line = "model_info.description = '{}'".format('New description for test')
+
+        # re-run function to test
+        self.widget.write_new_model_to_file(dummy_file_path, model1_name,
+                                            model2_name, symbol_operator)
+
+        # check content of dummy file path
+        with open(dummy_file_path, 'r') as f_in:
+            lines_from_generated_file = [line.strip() for line in f_in]
+
+        # update completed template
+        completed_template[4] = new_desc_line
+
+        # check content of generated file
+        for item in completed_template:
+            self.assertIn(item, lines_from_generated_file)
+
+    def testOnOperatorChange(self):
+        """
+        Test modification of displayed equation
+        when selected operator is changed
+        """
+
+        # check default
+        self.assertIn(self.widget.cbOperator.currentText(),
+                      self.widget.lblEquation.text())
+
+        # change operator
+        if self.widget.cbOperator.currentIndex() == 0:
+            self.widget.cbOperator.setCurrentIndex(1)
+        else:
+            self.widget.cbOperator.setCurrentIndex(0)
+
+        self.assertIn(self.widget.cbOperator.currentText(),
+                      self.widget.lblEquation.text())
+
+    def testOnHelp(self):
+        """ Test the default help renderer """
+
+        webbrowser.open = MagicMock()
+
+        # invoke the tested method
+        self.widget.onHelp()
+
+        # see that webbrowser open was attempted
+        webbrowser.open.assert_called()
+
+    def testOnNameCheck(self):
+        """ Test onNameCheck """
+
+        # Enter plugin name already present in list of existing models
+        self.widget.txtName.setText("cylinder")
+
+        # Scenario 1
+        # overwrite not checked -> message box should appear
+        # and good_name set to False, 'Apply' button disabled
+
+        # mock QMessageBox
+        QtWidgets.QMessageBox.critical = MagicMock()
+
+        self.widget.chkOverwrite.setChecked(False)
+        self.widget.txtName.editingFinished.emit()
+        self.assertFalse(self.widget.good_name)
+        self.assertFalse(self.widget.buttonBox.button(
+            QtWidgets.QDialogButtonBox.Apply).isEnabled())
+
+        self.assertTrue(QtWidgets.QMessageBox.critical.called_once())
+
+        msg = "Plugin with specified name already exists.\n"
+        msg += "Please specify different filename or allow file overwrite."
+
+        self.assertIn('Plugin Error', QtWidgets.QMessageBox.critical.call_args[0][1])
+        self.assertIn(msg, QtWidgets.QMessageBox.critical.call_args[0][2])
+
+        # Scenario 2
+        # overwrite checked -> no message box displayed
+        # and good_name set to True, Apply button enabled, output name created
+
+        # mock QMessageBox
+        QtWidgets.QMessageBox.critical = MagicMock()
+        # create dummy plugin_dir for output file
+        self.widget.plugin_dir = tempfile.gettempdir()
+
+        self.widget.chkOverwrite.setChecked(True)
+        self.widget.txtName.editingFinished.emit()
+        self.assertTrue(self.widget.good_name)
+        self.assertTrue(self.widget.buttonBox.button(
+            QtWidgets.QDialogButtonBox.Apply).isEnabled())
+
+        self.assertFalse(QtWidgets.QMessageBox.critical.called)
+
+        self.assertIn('cylinder.py', self.widget.plugin_filename)
+
+        # Scenario 3 Enter valid new plugin name -> filename created and Apply
+        # button enabled
+        # forbidding overwriting should not change anything
+        # since it's a new name
+        self.widget.txtName.setText("   cylinder0   ")
+        self.widget.chkOverwrite.setChecked(False)
+        self.widget.txtName.editingFinished.emit()
+        self.assertIn("cylinder0.py", self.widget.plugin_filename)
+        self.assertTrue(self.widget.buttonBox.button(
+            QtWidgets.QDialogButtonBox.Apply).isEnabled())
+
+    @patch.object(AddMultEditor, 'readModels')
+    def testOnUpdateModels(self, mock_new_list_models):
+        """ Test onUpdateModels """
+
+        ini_count_models = self.widget.cbModel1.count()
+
+        mock_new_list_models.return_value = ['cylinder', 'rpa',
+                                             'core_shell_cylinder', 'sphere',
+                                             'cylinder0']
+
+        self.widget.updateModels()
+        # check that the number of models in the comboboxes
+        # has been incremented by 1
+        self.assertEqual(self.widget.cbModel1.count(), ini_count_models + 1)
+        self.assertEqual(self.widget.cbModel2.count(), ini_count_models + 1)
+
+        # check that the new model is in the list
+        combobox = self.widget.cbModel1
+        self.assertIn('cylinder0', [combobox.itemText(indx)
+                                    for indx in range(combobox.count())])
+
+    def testOnReadModels(self):
+        """ The output of ReadModels is the return value of MagicMock defined
+        in the SetUp of these tests. """
+
+        self.assertEqual(self.widget.list_models, ['cylinder', 'rpa',
+                                                   'core_shell_cylinder',
+                                                   'sphere'])
+
+    def testCheckModel(self):
+        """ Test CheckModel"""
+
+        # TODO first: solve problem with empty 'test'
+        pass
