Hi Dave,

We've made updated our previous patch to fix the error messages.  Attached
are our updated patches.

On Mon, Mar 12, 2018 at 8:48 PM, Dave Page <dp...@pgadmin.org> wrote:

> Hi
>
> On Mon, Mar 12, 2018 at 5:18 PM, Joao De Almeida Pereira <
> jdealmeidapere...@pivotal.io> wrote:
>
>> Hi Dave and Murtuza,
>>
>> Regarding this patch we refactored the Javascript code so that is lives
>> in a different file and added some tests.
>>
>> Also we found an issue with karma-jasmine that does not allow us to use
>> jasmine 3.1 yet. You can find attached a patch that reverts that commit.
>>
>
> Sounds good, but neither patch will apply (in fact, the Jasmine one looks
> entirely backwards). One of the error messages was changed in Murtuza's
> patch, and wasn't reflected in your update for example.
>
> Can you rebase please?
>
> Thanks.
>
>
>>
>> Thanks
>> Victoria && Joao
>>
>> On Mon, Mar 12, 2018 at 4:46 PM Dave Page <dp...@pgadmin.org> wrote:
>>
>>> Thanks, patch applied!
>>>
>>> On Mon, Mar 12, 2018 at 3:31 AM, Murtuza Zabuawala <
>>> murtuza.zabuaw...@enterprisedb.com> wrote:
>>>
>>>> Hi Dave,
>>>>
>>>> PFA updated patch.
>>>>
>>>> --
>>>> Regards,
>>>> Murtuza Zabuawala
>>>> EnterpriseDB: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>>
>>>> On Fri, Mar 9, 2018 at 9:29 PM, Murtuza Zabuawala <
>>>> murtuza.zabuaw...@enterprisedb.com> wrote:
>>>>
>>>>> Hi Dave,
>>>>>
>>>>> I'll change the name and send you updated patch.
>>>>>
>>>>>
>>>>> On Fri, Mar 9, 2018 at 9:25 PM, Dave Page <dp...@pgadmin.org> wrote:
>>>>>
>>>>>> HI
>>>>>>
>>>>>> On Fri, Mar 9, 2018 at 11:47 AM, Murtuza Zabuawala <
>>>>>> murtuza.zabuaw...@enterprisedb.com> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> PFA patch to add service parameter in server dialog.
>>>>>>> - Docs updated
>>>>>>> - Test case added for Service ID parameter
>>>>>>>
>>>>>>> Please note,
>>>>>>> I have extracted Connection class and Server manager class from our
>>>>>>> own custom Psycopg2 driver module.
>>>>>>>
>>>>>>> Patch also covers RM#3120
>>>>>>>
>>>>>>
>>>>>>  This patch seems a little confused. The "Service" and "Service ID"
>>>>>> fields from pgAdmin 3 are very different things. The Redmine ticket seems
>>>>>> to be asking for the Service field (the pg_service.conf service name),
>>>>>> *not* Service ID (the operating system's service ID, used to start/stop 
>>>>>> the
>>>>>> database server service).
>>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/package.json b/web/package.json
index ad0f3e16..66684fc6 100644
--- a/web/package.json
+++ b/web/package.json
@@ -69,6 +69,7 @@
     "hard-source-webpack-plugin": "^0.4.9",
     "immutability-helper": "^2.2.0",
     "imports-loader": "^0.7.1",
+    "ip-address": "^5.8.9",
     "jquery": "1.11.2",
     "jquery-contextmenu": "^2.5.0",
     "jquery-ui": "^1.12.1",
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js
index 36908733..302fe458 100644
--- a/web/pgadmin/browser/server_groups/servers/static/js/server.js
+++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js
@@ -2,10 +2,13 @@ define('pgadmin.node.server', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
   'underscore.string', 'sources/pgadmin', 'pgadmin.browser',
   'pgadmin.server.supported_servers', 'pgadmin.user_management.current_user',
-  'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.browser.server.privilege',
+  'pgadmin.alertifyjs', 'pgadmin.backform',
+  'sources/browser/server_groups/servers/model_validation',
+  'pgadmin.browser.server.privilege',
 ], function(
   gettext, url_for, $, _, Backbone, S, pgAdmin, pgBrowser,
-  supported_servers, current_user, Alertify, Backform
+  supported_servers, current_user, Alertify, Backform,
+  modelValidation
 ) {
 
   if (!pgBrowser.Nodes['server']) {
@@ -848,110 +851,8 @@ define('pgadmin.node.server', [
           group: gettext('Connection'),
         }],
         validate: function() {
-          var err = {},
-            errmsg,
-            self = this;
-
-          var service_id = this.get('service');
-
-          var check_for_empty = function(id, msg) {
-            var v = self.get(id);
-            if (
-              _.isUndefined(v) || v === null || String(v).replace(/^\s+|\s+$/g, '') == ''
-            ) {
-              err[id] = msg;
-              errmsg = errmsg || msg;
-              return true;
-            } else {
-              self.errorModel.unset(id);
-              return false;
-            }
-          };
-          var check_for_valid_ipv6 = function(val){
-            // Regular expression for validating IPv6 address formats
-            var exps = ['^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|',
-              '(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|',
-              '2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|',
-              '(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|',
-              ':((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|',
-              '(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|',
-              '2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|',
-              '(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|',
-              '[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|',
-              '((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|',
-              '(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|',
-              '1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|',
-              '((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$'];
-
-            var exp = new RegExp(exps.join(''));
-            return exp.test(val.trim());
-          };
-          var check_for_valid_ip = function(id, msg) {
-            var v4exps = '(^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)';
-            var v4exp = new RegExp(v4exps);
-            var v = self.get(id);
-            if (
-              v && !(v4exp.test(v.trim()))
-            ) {
-              if(!check_for_valid_ipv6(v)){
-                err[id] = msg;
-                errmsg = msg;
-              }
-            } else {
-              self.errorModel.unset(id);
-            }
-          };
-
-          if (!self.isNew() && 'id' in self.sessAttrs) {
-            err['id'] = gettext('The ID cannot be changed.');
-            errmsg = err['id'];
-          } else {
-            self.errorModel.unset('id');
-          }
-          check_for_empty('name', gettext('Name must be specified.'));
-
-          // If no service id then only check
-          if (
-              _.isUndefined(service_id) || _.isNull(service_id) ||
-              String(service_id).replace(/^\s+|\s+$/g, '') == ''
-            ) {
-            if (check_for_empty(
-              'host', gettext('Either Host name, Address or Service must be specified.')
-            ) && check_for_empty('hostaddr', gettext('Either Host name, Address or Service must be specified.'))){
-              errmsg = errmsg || gettext('Either Host name, Address or Service must be specified.');
-            } else {
-              errmsg = undefined;
-              delete err['host'];
-              delete err['hostaddr'];
-            }
-
-            check_for_empty(
-              'db', gettext('Maintenance database must be specified.')
-            );
-            check_for_valid_ip(
-              'hostaddr', gettext('Host address must be valid IPv4 or IPv6 address.')
-            );
-            check_for_valid_ip(
-              'hostaddr', gettext('Host address must be valid IPv4 or IPv6 address.')
-            );
-          } else {
-            _.each(['host', 'hostaddr', 'db'], (item) => {
-              self.errorModel.unset(item);
-            });
-          }
-
-          check_for_empty(
-            'username', gettext('Username must be specified.')
-          );
-          check_for_empty('port', gettext('Port must be specified.'));
-
-          this.errorModel.set(err);
-
-          if (_.size(err)) {
-            return errmsg;
-          }
-
-          return null;
+          const validateModel = new modelValidation.ModelValidation(this);
+          return validateModel.validate();
         },
         isConnected: function(model) {
           return model.get('connected');
diff --git a/web/pgadmin/static/bundle/browser.js b/web/pgadmin/static/bundle/browser.js
index 3fcc69d8..83b2ad8b 100644
--- a/web/pgadmin/static/bundle/browser.js
+++ b/web/pgadmin/static/bundle/browser.js
@@ -1,6 +1,6 @@
 define('bundled_browser',[
   'pgadmin.browser',
-  'sources/browser/server_groups/servers/databases/external_tables/index',
+  'sources/browser/index',
 ], function(pgBrowser) {
   pgBrowser.init();
 });
diff --git a/web/pgadmin/static/js/browser/index.js b/web/pgadmin/static/js/browser/index.js
new file mode 100644
index 00000000..297e8bf9
--- /dev/null
+++ b/web/pgadmin/static/js/browser/index.js
@@ -0,0 +1,10 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import 'server_groups';
diff --git a/web/pgadmin/static/js/browser/server_groups/index.js b/web/pgadmin/static/js/browser/server_groups/index.js
new file mode 100644
index 00000000..b151b6f6
--- /dev/null
+++ b/web/pgadmin/static/js/browser/server_groups/index.js
@@ -0,0 +1,10 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import 'servers';
diff --git a/web/pgadmin/static/js/browser/server_groups/servers/databases/index.js b/web/pgadmin/static/js/browser/server_groups/servers/databases/index.js
new file mode 100644
index 00000000..ef17c0ad
--- /dev/null
+++ b/web/pgadmin/static/js/browser/server_groups/servers/databases/index.js
@@ -0,0 +1,10 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import 'external_tables';
diff --git a/web/pgadmin/static/js/browser/server_groups/servers/index.js b/web/pgadmin/static/js/browser/server_groups/servers/index.js
new file mode 100644
index 00000000..242a1919
--- /dev/null
+++ b/web/pgadmin/static/js/browser/server_groups/servers/index.js
@@ -0,0 +1,11 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import 'databases';
+import 'model_validation';
diff --git a/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js b/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
new file mode 100644
index 00000000..7ab129ba
--- /dev/null
+++ b/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
@@ -0,0 +1,104 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import _ from 'underscore';
+import {Address4, Address6} from 'ip-address';
+
+export class ModelValidation {
+  constructor(model) {
+    this.err = {};
+    this.errmsg = '';
+    this.model = model;
+  }
+
+  validate() {
+    const serviceId = this.model.get('service');
+
+    if (!this.model.isNew() && 'id' in this.model.sessAttrs) {
+      this.err['id'] = gettext('The ID cannot be changed.');
+      this.errmsg = this.err['id'];
+    } else {
+      this.model.errorModel.unset('id');
+    }
+
+    this.checkForEmpty('name', gettext('Name must be specified.'));
+
+    if (ModelValidation.isEmptyString(serviceId)) {
+      this.checkHostAndHostAddress();
+
+      this.checkForEmpty('db', gettext('Maintenance database must be specified.'));
+    } else {
+      this.clearHostAddressAndDbErrors();
+    }
+
+    this.checkForEmpty('username', gettext('Username must be specified.'));
+    this.checkForEmpty('port', gettext('Port must be specified.'));
+
+    this.model.errorModel.set(this.err);
+
+    if (_.size(this.err)) {
+      return this.errmsg;
+    }
+
+    return null;
+  }
+
+  clearHostAddressAndDbErrors() {
+    _.each(['host', 'hostaddr', 'db'], (item) => {
+      this.model.errorModel.unset(item);
+    });
+  }
+
+  checkHostAndHostAddress() {
+    const translatedStr = gettext('Either Host name, Address or Service must ' +
+      'be specified.');
+    if (this.checkForEmpty('host', translatedStr) &&
+      this.checkForEmpty('hostaddr', translatedStr)) {
+      this.errmsg = this.errmsg || translatedStr;
+    } else {
+      this.errmsg = undefined;
+      delete this.err['host'];
+      delete this.err['hostaddr'];
+    }
+
+    this.checkForValidIp(this.model.get('hostaddr'),
+      gettext('Host address must be valid IPv4 or IPv6 address.'));
+  }
+
+  checkForValidIp(ipAddress, msg) {
+    if (ipAddress) {
+      const isIpv6Address = new Address6(ipAddress).isValid();
+      const isIpv4Address = new Address4(ipAddress).isValid();
+      if (!isIpv4Address && !isIpv6Address) {
+        this.err['hostaddr'] = msg;
+        this.errmsg = msg;
+      }
+    } else {
+      this.model.errorModel.unset('hostaddr');
+    }
+  }
+
+  checkForEmpty(id, msg) {
+    const value = this.model.get(id);
+
+    if (ModelValidation.isEmptyString(value)) {
+      this.err[id] = msg;
+      this.errmsg = this.errmsg || msg;
+      return true;
+    } else {
+      this.model.errorModel.unset(id);
+      return false;
+    }
+  }
+
+  static isEmptyString(string) {
+    return _.isUndefined(string) || _.isNull(string) || string.trim() === '';
+  }
+}
diff --git a/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js b/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js
new file mode 100644
index 00000000..a05cd455
--- /dev/null
+++ b/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js
@@ -0,0 +1,101 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {ModelValidation} from 'sources/browser/server_groups/servers/model_validation';
+
+describe('Server#ModelValidation', () => {
+  describe('When validating a server parameters', () => {
+    let model;
+    let modelValidation;
+    beforeEach(() => {
+      model = {
+        errorModel: jasmine.createSpyObj('errorModel', ['set', 'unset']),
+        allValues: {},
+        get: function (key) {
+          return this.allValues[key];
+        },
+        sessAttrs: {},
+      };
+      model.isNew = jasmine.createSpy('isNew');
+      modelValidation = new ModelValidation(model);
+    });
+
+    describe('When all parameters are valid', () => {
+      beforeEach(() => {
+        model.isNew.and.returnValue(true);
+        model.allValues['name'] = 'some name';
+        model.allValues['username'] = 'some username';
+        model.allValues['port'] = 'some port';
+      });
+
+      describe('No service id', () => {
+        it('does not set any error in the model', () => {
+          model.allValues['host'] = 'some host';
+          model.allValues['db'] = 'some db';
+          model.allValues['hostaddr'] = '1.1.1.1';
+          expect(modelValidation.validate()).toBeNull();
+          expect(model.errorModel.set).toHaveBeenCalledWith({});
+        });
+      });
+
+      describe('Service id present', () => {
+        it('does not set any error in the model', () => {
+          model.allValues['service'] = 'asdfg';
+          expect(modelValidation.validate()).toBeNull();
+          expect(model.errorModel.set).toHaveBeenCalledWith({});
+        });
+      });
+    });
+
+    describe('When no parameters are valid', () => {
+      describe('Service id not present', () => {
+        it('does not set any error in the model', () => {
+          expect(modelValidation.validate()).toBe('Name must be specified.');
+          expect(model.errorModel.set).toHaveBeenCalledTimes(1);
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            name: 'Name must be specified.',
+            host: 'Either Host name, Address or Service must be specified.',
+            hostaddr: 'Either Host name, Address or Service must be specified.',
+            db: 'Maintenance database must be specified.',
+            username: 'Username must be specified.',
+            port: 'Port must be specified.'
+          });
+        });
+      });
+
+      describe('Host address is not valid', () => {
+        it('sets the "Host address must be a valid IPv4 or IPv6 address" error', () => {
+          model.allValues['hostaddr'] = 'something that is not an ip address';
+          expect(modelValidation.validate()).toBe('Host address must be valid IPv4 or IPv6 address.');
+          expect(model.errorModel.set).toHaveBeenCalledTimes(1);
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            name: 'Name must be specified.',
+            hostaddr: 'Host address must be valid IPv4 or IPv6 address.',
+            db: 'Maintenance database must be specified.',
+            username: 'Username must be specified.',
+            port: 'Port must be specified.'
+          });
+        });
+      });
+
+      describe('Service id present', () => {
+        it('does not set any error in the model', () => {
+          model.allValues['service'] = 'asdfg';
+          expect(modelValidation.validate()).toBe('Name must be specified.');
+          expect(model.errorModel.set).toHaveBeenCalledTimes(1);
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            name: 'Name must be specified.',
+            username: 'Username must be specified.',
+            port: 'Port must be specified.'
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/web/yarn.lock b/web/yarn.lock
index 85ccbc8b..2dc4c5c2 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -3938,6 +3938,18 @@ invert-kv@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6";
 
+ip-address@^5.8.9:
+  version "5.8.9"
+  resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-5.8.9.tgz#6379277c23fc5adb20511e4d23ec2c1bde105dfd";
+  dependencies:
+    jsbn "1.1.0"
+    lodash.find "^4.6.0"
+    lodash.max "^4.0.1"
+    lodash.merge "^4.6.0"
+    lodash.padstart "^4.6.1"
+    lodash.repeat "^4.1.0"
+    sprintf-js "1.1.0"
+
 ip-regex@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd";
@@ -4329,6 +4341,10 @@ js-yaml@~3.7.0:
     argparse "^1.0.7"
     esprima "^2.6.0"
 
+jsbn@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040";
+
 jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513";
@@ -4738,6 +4754,10 @@ lodash.escape@^3.0.0:
   dependencies:
     lodash._root "^3.0.0"
 
+lodash.find@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1";
+
 lodash.flattendeep@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2";
@@ -4777,6 +4797,10 @@ lodash.keys@^3.0.0:
     lodash.isarguments "^3.0.0"
     lodash.isarray "^3.0.0"
 
+lodash.max@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/lodash.max/-/lodash.max-4.0.1.tgz#8735566c618b35a9f760520b487ae79658af136a";
+
 lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe";
@@ -4785,10 +4809,22 @@ lodash.memoize@~3.0.3:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f";
 
+lodash.merge@^4.6.0:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54";
+
 lodash.mergewith@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55";
 
+lodash.padstart@^4.6.1:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b";
+
+lodash.repeat@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/lodash.repeat/-/lodash.repeat-4.1.0.tgz#fc7de8131d8c8ac07e4b49f74ffe829d1f2bec44";
+
 lodash.restparam@^3.0.0:
   version "3.6.1"
   resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805";
@@ -6863,6 +6899,10 @@ spectrum-colorpicker@^1.8.0:
   version "1.8.0"
   resolved "https://registry.yarnpkg.com/spectrum-colorpicker/-/spectrum-colorpicker-1.8.0.tgz#b926cf5002c0a77860b5f8351e1c093c65200107";
 
+sprintf-js@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.0.tgz#cffcaf702daf65ea39bb4e0fa2b299cec1a1be46";
+
 sprintf-js@^1.0.3:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c";
diff --git a/web/package.json b/web/package.json
index a4cb58d5..ad0f3e16 100644
--- a/web/package.json
+++ b/web/package.json
@@ -18,7 +18,7 @@
     "file-loader": "^0.11.2",
     "image-webpack-loader": "^3.3.1",
     "is-docker": "^1.1.0",
-    "jasmine-core": "~3.1.0",
+    "jasmine-core": "~2.99.0",
     "jasmine-enzyme": "~4.1.1",
     "karma": "~1.5.0",
     "karma-babel-preprocessor": "^6.0.1",
diff --git a/web/yarn.lock b/web/yarn.lock
index 5e310d79..85ccbc8b 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -4267,9 +4267,9 @@ isurl@^1.0.0-alpha5:
     has-to-string-tag-x "^1.2.0"
     is-object "^1.0.1"
 
-jasmine-core@~3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.1.0.tgz#a4785e135d5df65024dfc9224953df585bd2766c";
+jasmine-core@~2.99.0:
+  version "2.99.1"
+  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz#e6400df1e6b56e130b61c4bcd093daa7f6e8ca15";
 
 jasmine-enzyme@~4.1.1:
   version "4.1.1"

Reply via email to