Update sinon version

The WCT tests used deprecated version on sinon. This change adds
the newest version of sinon to polygerrit and update tests.

Change-Id: Ic746c4afc97779974da643362e17c9c1bb1cbf48
diff --git a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.js b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.js
index 491444a..72109f2 100644
--- a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.js
+++ b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.js
@@ -23,7 +23,6 @@
 
 suite('gr-admin-nav-behavior tests', () => {
   let element;
-  let sandbox;
   let capabilityStub;
   let menuLinkStub;
 
@@ -39,15 +38,10 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
     capabilityStub = sinon.stub();
     menuLinkStub = sinon.stub().returns([]);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   const testAdminLinks = (account, options, expected, done) => {
     element.getAdminLinks(account,
         capabilityStub,
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.js b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.js
index 50a3316..1f144a0 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.js
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.js
@@ -23,7 +23,6 @@
 
 suite('gr-tooltip-behavior tests', () => {
   let element;
-  let sandbox;
 
   function makeTooltip(tooltipRect, parentRect) {
     return {
@@ -45,16 +44,11 @@
   });
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('normal position', () => {
-    sandbox.stub(element, 'getBoundingClientRect', () => {
+    sinon.stub(element, 'getBoundingClientRect').callsFake(() => {
       return {top: 100, left: 100, width: 200};
     });
     const tooltip = makeTooltip(
@@ -68,7 +62,7 @@
   });
 
   test('left side position', () => {
-    sandbox.stub(element, 'getBoundingClientRect', () => {
+    sinon.stub(element, 'getBoundingClientRect').callsFake(() => {
       return {top: 100, left: 10, width: 50};
     });
     const tooltip = makeTooltip(
@@ -85,7 +79,7 @@
   });
 
   test('right side position', () => {
-    sandbox.stub(element, 'getBoundingClientRect', () => {
+    sinon.stub(element, 'getBoundingClientRect').callsFake(() => {
       return {top: 100, left: 950, width: 50};
     });
     const tooltip = makeTooltip(
@@ -102,7 +96,7 @@
   });
 
   test('position to bottom', () => {
-    sandbox.stub(element, 'getBoundingClientRect', () => {
+    sinon.stub(element, 'getBoundingClientRect').callsFake(() => {
       return {top: 100, left: 950, width: 50, height: 50};
     });
     const tooltip = makeTooltip(
@@ -120,20 +114,20 @@
   });
 
   test('hides tooltip when detached', () => {
-    sandbox.stub(element, '_handleHideTooltip');
+    sinon.stub(element, '_handleHideTooltip');
     element.remove();
     flushAsynchronousOperations();
     assert.isTrue(element._handleHideTooltip.called);
   });
 
   test('sets up listeners when has-tooltip is changed', () => {
-    const addListenerStub = sandbox.stub(element, 'addEventListener');
+    const addListenerStub = sinon.stub(element, 'addEventListener');
     element.hasTooltip = true;
     assert.isTrue(addListenerStub.called);
   });
 
   test('clean up listeners when has-tooltip changed to false', () => {
-    const removeListenerStub = sandbox.stub(element, 'removeEventListener');
+    const removeListenerStub = sinon.stub(element, 'removeEventListener');
     element.hasTooltip = true;
     element.hasTooltip = false;
     assert.isTrue(removeListenerStub.called);
diff --git a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.js b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.js
index f62f50b..1fe2874 100644
--- a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.js
+++ b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.js
@@ -24,7 +24,6 @@
 
 suite('gr-url-encoding-behavior tests', () => {
   let element;
-  let sandbox;
 
   suiteSetup(() => {
     // Define a Polymer element that uses this behavior.
@@ -35,14 +34,9 @@
   });
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('encodeURL', () => {
     test('double encodes', () => {
       assert.equal(element.encodeURL('abc?123'), 'abc%253F123');
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.js b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.js
index c3bcccc..30231ce 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.js
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.js
@@ -35,7 +35,6 @@
 
   let element;
   let overlay;
-  let sandbox;
 
   suiteSetup(() => {
     // Define a Polymer element that uses this behavior.
@@ -53,11 +52,6 @@
   setup(() => {
     element = basicFixture.instantiate();
     overlay = withinOverlayFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   suite('ShortcutManager', () => {
@@ -337,7 +331,7 @@
   });
 
   test('modifierPressed returns accurate values', () => {
-    const spy = sandbox.spy(element, 'modifierPressed');
+    const spy = sinon.spy(element, 'modifierPressed');
     element._handleKey = e => {
       element.modifierPressed(e);
     };
@@ -358,7 +352,7 @@
   });
 
   test('isModifierPressed returns accurate value', () => {
-    const spy = sandbox.spy(element, 'isModifierPressed');
+    const spy = sinon.spy(element, 'isModifierPressed');
     element._handleKey = e => {
       element.isModifierPressed(e, 'shiftKey');
     };
@@ -384,12 +378,12 @@
     setup(() => {
       element._shortcut_go_table.set('a', '_handleA');
       handlerStub = element._handleA = sinon.stub();
-      sandbox.stub(Date, 'now').returns(10000);
+      sinon.stub(Date, 'now').returns(10000);
     });
 
     test('success', () => {
       const e = {detail: {key: 'a'}, preventDefault: () => {}};
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
       element._shortcut_go_key_last_pressed = 9000;
       element._handleGoAction(e);
       assert.isTrue(handlerStub.calledOnce);
@@ -398,7 +392,7 @@
 
     test('go key not pressed', () => {
       const e = {detail: {key: 'a'}, preventDefault: () => {}};
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
       element._shortcut_go_key_last_pressed = null;
       element._handleGoAction(e);
       assert.isFalse(handlerStub.called);
@@ -406,7 +400,7 @@
 
     test('go key pressed too long ago', () => {
       const e = {detail: {key: 'a'}, preventDefault: () => {}};
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
       element._shortcut_go_key_last_pressed = 3000;
       element._handleGoAction(e);
       assert.isFalse(handlerStub.called);
@@ -414,7 +408,7 @@
 
     test('should suppress', () => {
       const e = {detail: {key: 'a'}, preventDefault: () => {}};
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(true);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(true);
       element._shortcut_go_key_last_pressed = 9000;
       element._handleGoAction(e);
       assert.isFalse(handlerStub.called);
@@ -422,7 +416,7 @@
 
     test('unrecognized key', () => {
       const e = {detail: {key: 'f'}, preventDefault: () => {}};
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
       element._shortcut_go_key_last_pressed = 9000;
       element._handleGoAction(e);
       assert.isFalse(handlerStub.called);
diff --git a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.js b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.js
index ae37134..0e0ff2e 100644
--- a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.js
+++ b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.js
@@ -23,7 +23,6 @@
 
 suite('gr-tooltip-behavior tests', () => {
   let element;
-  let sandbox;
 
   suiteSetup(() => {
     Polymer({
@@ -33,14 +32,9 @@
   });
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('SafeUrl accepts valid urls', () => {
     function accepts(url) {
       const safeUrl = new element.SafeUrl(url);
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.js b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.js
index 2128e29..2d7096f 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.js
@@ -22,17 +22,11 @@
 
 suite('gr-access-section tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = fixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('unit tests', () => {
     setup(() => {
       element.section = {
@@ -123,7 +117,7 @@
     });
 
     test('_computePermissions', () => {
-      sandbox.stub(element, 'toSortedArray').returns(
+      sinon.stub(element, 'toSortedArray').returns(
           [{
             id: 'push',
             value: {
@@ -463,7 +457,7 @@
 
       test('_handleValueChange', () => {
         // For an existing section.
-        const modifiedHandler = sandbox.stub();
+        const modifiedHandler = sinon.stub();
         element.section = {id: 'refs/for/bar', value: {permissions: {}}};
         assert.notOk(element.section.value.updatedId);
         element.section.id = 'refs/for/baz';
@@ -528,7 +522,7 @@
       });
 
       test('remove an added section', () => {
-        const removeStub = sandbox.stub();
+        const removeStub = sinon.stub();
         element.addEventListener('added-section-removed', removeStub);
         element.editing = true;
         element.section.value.added = true;
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.js
index 973f3df..546b8a8 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.js
@@ -40,20 +40,15 @@
 suite('gr-admin-group-list tests', () => {
   let element;
   let groups;
-  let sandbox;
+
   let value;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_computeGroupUrl', () => {
-    let urlStub = sandbox.stub(GerritNav, 'getUrlForGroup',
+    let urlStub = sinon.stub(GerritNav, 'getUrlForGroup').callsFake(
         () => '/admin/groups/e2cd66f88a2db4d391ac068a92d987effbe872f5');
 
     let group = {
@@ -64,7 +59,7 @@
 
     urlStub.restore();
 
-    urlStub = sandbox.stub(GerritNav, 'getUrlForGroup',
+    urlStub = sinon.stub(GerritNav, 'getUrlForGroup').callsFake(
         () => '/admin/groups/user/test');
 
     group = {
@@ -102,7 +97,7 @@
     });
 
     test('_maybeOpenCreateOverlay', () => {
-      const overlayOpen = sandbox.stub(element.$.createOverlay, 'open');
+      const overlayOpen = sinon.stub(element.$.createOverlay, 'open');
       element._maybeOpenCreateOverlay();
       assert.isFalse(overlayOpen.called);
       const params = {};
@@ -134,10 +129,10 @@
 
   suite('filter', () => {
     test('_paramsChanged', done => {
-      sandbox.stub(
+      sinon.stub(
           element.$.restAPI,
-          'getGroups',
-          () => Promise.resolve(groups));
+          'getGroups')
+          .callsFake(() => Promise.resolve(groups));
       const value = {
         filter: 'test',
         offset: 25,
@@ -167,7 +162,7 @@
 
   suite('create new', () => {
     test('_handleCreateClicked called when create-click fired', () => {
-      sandbox.stub(element, '_handleCreateClicked');
+      sinon.stub(element, '_handleCreateClicked');
       element.shadowRoot
           .querySelector('gr-list-view').dispatchEvent(
               new CustomEvent('create-clicked', {
@@ -177,13 +172,13 @@
     });
 
     test('_handleCreateClicked opens modal', () => {
-      const openStub = sandbox.stub(element.$.createOverlay, 'open');
+      const openStub = sinon.stub(element.$.createOverlay, 'open');
       element._handleCreateClicked();
       assert.isTrue(openStub.called);
     });
 
     test('_handleCreateGroup called when confirm fired', () => {
-      sandbox.stub(element, '_handleCreateGroup');
+      sinon.stub(element, '_handleCreateGroup');
       element.$.createDialog.dispatchEvent(
           new CustomEvent('confirm', {
             composed: true, bubbles: true,
@@ -192,7 +187,7 @@
     });
 
     test('_handleCloseCreate called when cancel fired', () => {
-      sandbox.stub(element, '_handleCloseCreate');
+      sinon.stub(element, '_handleCloseCreate');
       element.$.createDialog.dispatchEvent(
           new CustomEvent('cancel', {
             composed: true, bubbles: true,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
index 6d47c5e..25b14e2 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
@@ -25,10 +25,8 @@
 
 suite('gr-admin-view tests', () => {
   let element;
-  let sandbox;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     stub('gr-rest-api-interface', {
       getProjectConfig() {
@@ -36,14 +34,10 @@
       },
     });
     const pluginsLoaded = Promise.resolve();
-    sandbox.stub(pluginLoader, 'awaitPluginsLoaded').returns(pluginsLoaded);
+    sinon.stub(pluginLoader, 'awaitPluginsLoaded').returns(pluginsLoaded);
     pluginsLoaded.then(() => flush(done));
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_computeURLHelper', () => {
     const path = '/test';
     const host = 'http://www.testsite.com';
@@ -56,7 +50,7 @@
         element._computeLinkURL({url: '/test', noBaseUrl: true}),
         '//' + window.location.host + '/test');
 
-    sandbox.stub(element, 'getBaseUrl').returns('/foo');
+    sinon.stub(element, 'getBaseUrl').returns('/foo');
     assert.equal(
         element._computeLinkURL({url: '/test', noBaseUrl: true}),
         '//' + window.location.host + '/foo/test');
@@ -88,18 +82,18 @@
   });
 
   test('_filteredLinks admin', done => {
-    sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
+    sinon.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
       name: 'test-user',
     }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({
           createGroup: true,
           createProject: true,
           viewPlugins: true,
         })
-    );
+        );
     element.reload().then(() => {
       assert.equal(element._filteredLinks.length, 3);
 
@@ -116,14 +110,14 @@
   });
 
   test('_filteredLinks non admin authenticated', done => {
-    sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
+    sinon.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
       name: 'test-user',
     }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({})
-    );
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({})
+        );
     element.reload().then(() => {
       assert.equal(element._filteredLinks.length, 2);
 
@@ -147,7 +141,7 @@
   });
 
   test('_filteredLinks from plugin', () => {
-    sandbox.stub(element.$.jsAPI, 'getAdminMenuLinks').returns([
+    sinon.stub(element.$.jsAPI, 'getAdminMenuLinks').returns([
       {text: 'internal link text', url: '/internal/link/url'},
       {text: 'external link text', url: 'http://external/link/url'},
     ]);
@@ -176,13 +170,13 @@
 
   test('Repo shows up in nav', done => {
     element._repoName = 'Test Repo';
-    sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
+    sinon.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
       name: 'test-user',
     }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({
           createGroup: true,
           createProject: true,
           viewPlugins: true,
@@ -207,13 +201,13 @@
     element._groupIsInternal = true;
     element._isAdmin = true;
     element._groupOwner = false;
-    sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
+    sinon.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
       name: 'test-user',
     }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({
           createGroup: true,
           createProject: true,
           viewPlugins: true,
@@ -236,19 +230,19 @@
   });
 
   test('Nav is reloaded when repo changes', () => {
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({
           createGroup: true,
           createProject: true,
           viewPlugins: true,
         }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccount',
-        () => Promise.resolve({_id: 1}));
-    sandbox.stub(element, 'reload');
+        'getAccount')
+        .callsFake(() => Promise.resolve({_id: 1}));
+    sinon.stub(element, 'reload');
     element.params = {repo: 'Test Repo', adminView: 'gr-repo'};
     assert.equal(element.reload.callCount, 1);
     element.params = {repo: 'Test Repo 2',
@@ -257,28 +251,28 @@
   });
 
   test('Nav is reloaded when group changes', () => {
-    sandbox.stub(element, '_computeGroupName');
-    sandbox.stub(
+    sinon.stub(element, '_computeGroupName');
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({
           createGroup: true,
           createProject: true,
           viewPlugins: true,
         }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccount',
-        () => Promise.resolve({_id: 1}));
-    sandbox.stub(element, 'reload');
+        'getAccount')
+        .callsFake(() => Promise.resolve({_id: 1}));
+    sinon.stub(element, 'reload');
     element.params = {groupId: '1', adminView: 'gr-group'};
     assert.equal(element.reload.callCount, 1);
   });
 
   test('Nav is reloaded when group name changes', done => {
     const newName = 'newName';
-    sandbox.stub(element, '_computeGroupName');
-    sandbox.stub(element, 'reload', () => {
+    sinon.stub(element, '_computeGroupName');
+    sinon.stub(element, 'reload').callsFake(() => {
       assert.equal(element._groupName, newName);
       assert.isTrue(element.reload.called);
       done();
@@ -325,18 +319,18 @@
       view: GerritNav.View.REPO,
       detail: GerritNav.RepoDetailView.ACCESS,
     };
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccountCapabilities',
-        () => Promise.resolve({
+        'getAccountCapabilities')
+        .callsFake(() => Promise.resolve({
           createGroup: true,
           createProject: true,
           viewPlugins: true,
         }));
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getAccount',
-        () => Promise.resolve({_id: 1}));
+        'getAccount')
+        .callsFake(() => Promise.resolve({_id: 1}));
     flushAsynchronousOperations();
     const expectedFilteredLinks = [
       {
@@ -449,9 +443,9 @@
         parent: 'my-repo',
       },
     ];
-    sandbox.stub(GerritNav, 'navigateToRelativeUrl');
-    sandbox.spy(element, '_selectedIsCurrentPage');
-    sandbox.spy(element, '_handleSubsectionChange');
+    sinon.stub(GerritNav, 'navigateToRelativeUrl');
+    sinon.spy(element, '_selectedIsCurrentPage');
+    sinon.spy(element, '_handleSubsectionChange');
     element.reload().then(() => {
       assert.deepEqual(element._filteredLinks, expectedFilteredLinks);
       assert.deepEqual(element._subsectionLinks, expectedSubsectionLinks);
@@ -488,18 +482,18 @@
 
   suite('_computeSelectedClass', () => {
     setup(() => {
-      sandbox.stub(
+      sinon.stub(
           element.$.restAPI,
-          'getAccountCapabilities',
-          () => Promise.resolve({
+          'getAccountCapabilities')
+          .callsFake(() => Promise.resolve({
             createGroup: true,
             createProject: true,
             viewPlugins: true,
           }));
-      sandbox.stub(
+      sinon.stub(
           element.$.restAPI,
-          'getAccount',
-          () => Promise.resolve({_id: 1}));
+          'getAccount')
+          .callsFake(() => Promise.resolve({_id: 1}));
 
       return element.reload();
     });
@@ -581,12 +575,12 @@
           _loadGroupDetails: () => {},
         });
 
-        sandbox.stub(element.$.restAPI, 'getGroupConfig')
+        sinon.stub(element.$.restAPI, 'getGroupConfig')
             .returns(Promise.resolve({
               name: 'foo',
               id: 'c0f83e941ce90caea30e6ad88f0d4ea0e841a7a9',
             }));
-        sandbox.stub(element.$.restAPI, 'getIsGroupOwner')
+        sinon.stub(element.$.restAPI, 'getIsGroupOwner')
             .returns(Promise.resolve(true));
         return element.reload();
       });
@@ -625,7 +619,7 @@
 
       test('external group', () => {
         element.$.restAPI.getGroupConfig.restore();
-        sandbox.stub(element.$.restAPI, 'getGroupConfig')
+        sinon.stub(element.$.restAPI, 'getGroupConfig')
             .returns(Promise.resolve({
               name: 'foo',
               id: 'external-id',
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.js
index e00c23d..485a48b 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.js
@@ -22,21 +22,15 @@
 
 suite('gr-confirm-delete-item-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_handleConfirmTap', () => {
-    const confirmHandler = sandbox.stub();
+    const confirmHandler = sinon.stub();
     element.addEventListener('confirm', confirmHandler);
-    sandbox.spy(element, '_handleConfirmTap');
+    sinon.spy(element, '_handleConfirmTap');
     element.shadowRoot
         .querySelector('gr-dialog').dispatchEvent(
             new CustomEvent('confirm', {
@@ -49,9 +43,9 @@
   });
 
   test('_handleCancelTap', () => {
-    const cancelHandler = sandbox.stub();
+    const cancelHandler = sinon.stub();
     element.addEventListener('cancel', cancelHandler);
-    sandbox.spy(element, '_handleCancelTap');
+    sinon.spy(element, '_handleCancelTap');
     element.shadowRoot
         .querySelector('gr-dialog').dispatchEvent(
             new CustomEvent('cancel', {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.js
index b87783e..07eee42 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.js
@@ -22,10 +22,8 @@
 
 suite('gr-create-change-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(true); },
       getRepoBranches(input) {
@@ -52,10 +50,6 @@
     };
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('new change created with default', done => {
     const configInputObj = {
       branch: 'test-branch',
@@ -65,8 +59,8 @@
       work_in_progress: true,
     };
 
-    const saveStub = sandbox.stub(element.$.restAPI,
-        'createChange', () => Promise.resolve({}));
+    const saveStub = sinon.stub(element.$.restAPI,
+        'createChange').callsFake(() => Promise.resolve({}));
 
     element.branch = 'test-branch';
     element.topic = 'test-topic';
@@ -92,7 +86,8 @@
       configured_value: 'TRUE',
       inherited_value: false,
     };
-    sandbox.stub(element, '_formatBooleanString', () => Promise.resolve(true));
+    sinon.stub(element, '_formatBooleanString')
+        .callsFake(() => Promise.resolve(true));
     flushAsynchronousOperations();
 
     const configInputObj = {
@@ -103,8 +98,8 @@
       work_in_progress: true,
     };
 
-    const saveStub = sandbox.stub(element.$.restAPI,
-        'createChange', () => Promise.resolve({}));
+    const saveStub = sinon.stub(element.$.restAPI,
+        'createChange').callsFake(() => Promise.resolve({}));
 
     element.branch = 'test-branch';
     element.topic = 'test-topic';
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.js b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.js
index cf17f24..d9bc500 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.js
@@ -23,21 +23,16 @@
 
 suite('gr-create-group-dialog tests', () => {
   let element;
-  let sandbox;
+
   const GROUP_NAME = 'test-group';
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(true); },
     });
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('name is updated correctly', done => {
     assert.isFalse(element.hasNewGroupName);
 
@@ -52,13 +47,13 @@
   });
 
   test('test for redirecting to group on successful creation', done => {
-    sandbox.stub(element.$.restAPI, 'createGroup')
+    sinon.stub(element.$.restAPI, 'createGroup')
         .returns(Promise.resolve({status: 201}));
 
-    sandbox.stub(element.$.restAPI, 'getGroupConfig')
+    sinon.stub(element.$.restAPI, 'getGroupConfig')
         .returns(Promise.resolve({group_id: 551}));
 
-    const showStub = sandbox.stub(page, 'show');
+    const showStub = sinon.stub(page, 'show');
     element.handleCreateGroup()
         .then(() => {
           assert.isTrue(showStub.calledWith('/admin/groups/551'));
@@ -67,13 +62,13 @@
   });
 
   test('test for unsuccessful group creation', done => {
-    sandbox.stub(element.$.restAPI, 'createGroup')
+    sinon.stub(element.$.restAPI, 'createGroup')
         .returns(Promise.resolve({status: 409}));
 
-    sandbox.stub(element.$.restAPI, 'getGroupConfig')
+    sinon.stub(element.$.restAPI, 'getGroupConfig')
         .returns(Promise.resolve({group_id: 551}));
 
-    const showStub = sandbox.stub(page, 'show');
+    const showStub = sinon.stub(page, 'show');
     element.handleCreateGroup()
         .then(() => {
           assert.isFalse(showStub.called);
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.js
index 93d60ae..22f19a6 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.js
@@ -23,29 +23,23 @@
 
 suite('gr-create-pointer-dialog tests', () => {
   let element;
-  let sandbox;
 
   const ironInput = function(element) {
     return dom(element).querySelector('iron-input');
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(true); },
     });
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('branch created', done => {
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'createRepoBranch',
-        () => Promise.resolve({}));
+        'createRepoBranch')
+        .callsFake(() => Promise.resolve({}));
 
     assert.isFalse(element.hasNewItemName);
 
@@ -64,10 +58,10 @@
   });
 
   test('tag created', done => {
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'createRepoTag',
-        () => Promise.resolve({}));
+        'createRepoTag')
+        .callsFake(() => Promise.resolve({}));
 
     assert.isFalse(element.hasNewItemName);
 
@@ -86,10 +80,10 @@
   });
 
   test('tag created with annotations', done => {
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'createRepoTag',
-        () => Promise.resolve({}));
+        'createRepoTag')
+        .callsFake(() => Promise.resolve({}));
 
     assert.isFalse(element.hasNewItemName);
 
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.js
index b57bf25..1e1fb0e 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.js
@@ -22,20 +22,14 @@
 
 suite('gr-create-repo-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(true); },
     });
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('default values are populated', () => {
     assert.isTrue(element.$.initialCommit.bindValue);
     assert.isFalse(element.$.parentRepo.bindValue);
@@ -50,8 +44,8 @@
       owners: ['testId'],
     };
 
-    const saveStub = sandbox.stub(element.$.restAPI,
-        'createRepo', () => Promise.resolve({}));
+    const saveStub = sinon.stub(element.$.restAPI,
+        'createRepo').callsFake(() => Promise.resolve({}));
 
     assert.isFalse(element.hasNewRepoName);
 
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.js
index fdf073e..1bbfcae 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.js
@@ -22,17 +22,11 @@
 
 suite('gr-group-audit-log tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('members', () => {
     test('test _getNameForGroup', () => {
       let group = {
@@ -85,8 +79,9 @@
       element.groupId = 1;
 
       const response = {status: 404};
-      sandbox.stub(
-          element.$.restAPI, 'getGroupAuditLog', (group, errFn) => {
+      sinon.stub(
+          element.$.restAPI, 'getGroupAuditLog')
+          .callsFake((group, errFn) => {
             errFn(response);
           });
 
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
index 90228cc..f047cfe 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
@@ -23,15 +23,13 @@
 
 suite('gr-group-members tests', () => {
   let element;
-  let sandbox;
+
   let groups;
   let groupMembers;
   let includedGroups;
   let groupStub;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     groups = {
       name: 'Administrators',
       owner: 'Administrators',
@@ -137,19 +135,15 @@
       },
     });
     element = basicFixture.instantiate();
-    sandbox.stub(element, 'getBaseUrl').returns('https://test/site');
+    sinon.stub(element, 'getBaseUrl').returns('https://test/site');
     element.groupId = 1;
-    groupStub = sandbox.stub(
+    groupStub = sinon.stub(
         element.$.restAPI,
-        'getGroupConfig',
-        () => Promise.resolve(groups));
+        'getGroupConfig')
+        .callsFake(() => Promise.resolve(groups));
     return element._loadGroupDetails();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_includedGroups', () => {
     assert.equal(element._includedGroups.length, 3);
     assert.equal(dom(element.root)
@@ -167,8 +161,8 @@
 
     const memberName = 'test-admin';
 
-    const saveStub = sandbox.stub(element.$.restAPI, 'saveGroupMembers',
-        () => Promise.resolve({}));
+    const saveStub = sinon.stub(element.$.restAPI, 'saveGroupMembers')
+        .callsFake(() => Promise.resolve({}));
 
     const button = element.$.saveGroupMember;
 
@@ -192,8 +186,9 @@
 
     const includedGroupName = 'testName';
 
-    const saveIncludedGroupStub = sandbox.stub(
-        element.$.restAPI, 'saveIncludedGroup', () => Promise.resolve({}));
+    const saveIncludedGroupStub = sinon.stub(
+        element.$.restAPI, 'saveIncludedGroup')
+        .callsFake(() => Promise.resolve({}));
 
     const button = element.$.saveIncludedGroups;
 
@@ -216,11 +211,11 @@
     element._groupOwner = true;
 
     const memberName = 'bad-name';
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     element.addEventListener('show-alert', alertStub);
     const error = new Error('error');
     error.status = 404;
-    sandbox.stub(element.$.restAPI, 'saveGroupMembers',
+    sinon.stub(element.$.restAPI, 'saveGroupMembers').callsFake(
         () => Promise.reject(error));
 
     element.$.groupMemberSearchInput.text = memberName;
@@ -345,8 +340,9 @@
     element.groupId = 1;
 
     const response = {status: 404};
-    sandbox.stub(
-        element.$.restAPI, 'getGroupConfig', (group, errFn) => {
+    sinon.stub(
+        element.$.restAPI, 'getGroupConfig')
+        .callsFake((group, errFn) => {
           errFn(response);
         });
     element.addEventListener('page-error', e => {
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
index 0cb5dfe..c6054b2 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
@@ -22,7 +22,7 @@
 
 suite('gr-group tests', () => {
   let element;
-  let sandbox;
+
   let groupStub;
   const group = {
     id: '6a1e70e1a88782771a91808c8af9bbb7a9871389',
@@ -36,20 +36,14 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(true); },
     });
     element = basicFixture.instantiate();
-    groupStub = sandbox.stub(
+    groupStub = sinon.stub(
         element.$.restAPI,
-        'getGroupConfig',
-        () => Promise.resolve(group)
-    );
-  });
-
-  teardown(() => {
-    sandbox.restore();
+        'getGroupConfig')
+        .callsFake(() => Promise.resolve(group));
   });
 
   test('loading displays before group config is loaded', () => {
@@ -61,10 +55,10 @@
   });
 
   test('default values are populated with internal group', done => {
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getIsGroupOwner',
-        () => Promise.resolve(true));
+        'getIsGroupOwner')
+        .callsFake(() => Promise.resolve(true));
     element.groupId = 1;
     element._loadGroup().then(() => {
       assert.isTrue(element._groupIsInternal);
@@ -77,14 +71,14 @@
     const groupExternal = Object.assign({}, group);
     groupExternal.id = 'external-group-id';
     groupStub.restore();
-    groupStub = sandbox.stub(
+    groupStub = sinon.stub(
         element.$.restAPI,
-        'getGroupConfig',
-        () => Promise.resolve(groupExternal));
-    sandbox.stub(
+        'getGroupConfig')
+        .callsFake(() => Promise.resolve(groupExternal));
+    sinon.stub(
         element.$.restAPI,
-        'getIsGroupOwner',
-        () => Promise.resolve(true));
+        'getIsGroupOwner')
+        .callsFake(() => Promise.resolve(true));
     element.groupId = 1;
     element._loadGroup().then(() => {
       assert.isFalse(element._groupIsInternal);
@@ -102,15 +96,15 @@
     };
     element._groupName = groupName;
 
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getIsGroupOwner',
-        () => Promise.resolve(true));
+        'getIsGroupOwner')
+        .callsFake(() => Promise.resolve(true));
 
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'saveGroupName',
-        () => Promise.resolve({status: 200}));
+        'saveGroupName')
+        .callsFake(() => Promise.resolve({status: 200}));
 
     const button = element.$.inputUpdateNameBtn;
 
@@ -141,10 +135,10 @@
     element._groupConfigOwner = 'testId';
     element._groupOwner = true;
 
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getIsGroupOwner',
-        () => Promise.resolve({status: 200}));
+        'getIsGroupOwner')
+        .callsFake(() => Promise.resolve({status: 200}));
 
     const button = element.$.inputUpdateOwnerBtn;
 
@@ -168,10 +162,10 @@
   test('test for undefined group name', done => {
     groupStub.restore();
 
-    sandbox.stub(
+    sinon.stub(
         element.$.restAPI,
-        'getGroupConfig',
-        () => Promise.resolve({}));
+        'getGroupConfig')
+        .callsFake(() => Promise.resolve({}));
 
     assert.isUndefined(element.groupId);
 
@@ -195,10 +189,10 @@
       name: 'test-group',
     };
 
-    sandbox.stub(element.$.restAPI, 'saveGroupName')
+    sinon.stub(element.$.restAPI, 'saveGroupName')
         .returns(Promise.resolve({status: 200}));
 
-    const showStub = sandbox.stub(element, 'dispatchEvent');
+    const showStub = sinon.stub(element, 'dispatchEvent');
     element._handleSaveName()
         .then(() => {
           assert.isTrue(showStub.called);
@@ -245,10 +239,10 @@
     element.groupId = 1;
 
     const response = {status: 404};
-    sandbox.stub(
-        element.$.restAPI, 'getGroupConfig', (group, errFn) => {
-          errFn(response);
-        });
+    sinon.stub(
+        element.$.restAPI, 'getGroupConfig').callsFake((group, errFn) => {
+      errFn(response);
+    });
 
     element.addEventListener('page-error', e => {
       assert.deepEqual(e.detail.response, response);
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.js
index 1d9dacc..835c90a 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.js
@@ -22,12 +22,10 @@
 
 suite('gr-permission tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
-    sandbox.stub(element.$.restAPI, 'getSuggestedGroups').returns(
+    sinon.stub(element.$.restAPI, 'getSuggestedGroups').returns(
         Promise.resolve({
           'Administrators': {
             id: '4c97682e6ce61b7247f3381b6f1789356666de7f',
@@ -38,10 +36,6 @@
         }));
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('unit tests', () => {
     test('_sortPermission', () => {
       const permission = {
@@ -253,7 +247,7 @@
 
   suite('interactions', () => {
     setup(() => {
-      sandbox.spy(element, '_computeLabel');
+      sinon.spy(element, '_computeLabel');
       element.name = 'Priority';
       element.section = 'refs/*';
       element.labels = {
@@ -337,7 +331,7 @@
     });
 
     test('removing an added permission', () => {
-      const removeStub = sandbox.stub();
+      const removeStub = sinon.stub();
       element.addEventListener('added-permission-removed', removeStub);
       element.editing = true;
       element.name = 'Priority';
@@ -352,7 +346,7 @@
       element.name = 'Priority';
       element.section = 'refs/*';
 
-      const removeStub = sandbox.stub();
+      const removeStub = sinon.stub();
       element.addEventListener('added-permission-removed', removeStub);
 
       assert.isFalse(element.$.permission.classList.contains('deleted'));
@@ -384,7 +378,7 @@
     });
 
     test('_handleValueChange', () => {
-      const modifiedHandler = sandbox.stub();
+      const modifiedHandler = sinon.stub();
       element.permission = {value: {rules: {}}};
       element.addEventListener('access-modified', modifiedHandler);
       assert.isNotOk(element.permission.value.modified);
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.js b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.js
index 5791260..9e9eb1c 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.js
@@ -23,13 +23,12 @@
 
 suite('gr-plugin-config-array-editor tests', () => {
   let element;
-  let sandbox;
+
   let dispatchStub;
 
   const getAll = str => dom(element.root).querySelectorAll(str);
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.pluginOption = {
       _key: 'test-key',
@@ -39,8 +38,6 @@
     };
   });
 
-  teardown(() => sandbox.restore());
-
   test('_computeShowInputRow', () => {
     assert.equal(element._computeShowInputRow(true), 'hide');
     assert.equal(element._computeShowInputRow(false), '');
@@ -58,7 +55,7 @@
 
   suite('adding', () => {
     setup(() => {
-      dispatchStub = sandbox.stub(element, '_dispatchChanged');
+      dispatchStub = sinon.stub(element, '_dispatchChanged');
     });
 
     test('with enter', () => {
@@ -93,7 +90,7 @@
   });
 
   test('deleting', () => {
-    dispatchStub = sandbox.stub(element, '_dispatchChanged');
+    dispatchStub = sinon.stub(element, '_dispatchChanged');
     element.pluginOption = {info: {values: ['test', 'test2']}};
     flushAsynchronousOperations();
 
@@ -117,7 +114,7 @@
   });
 
   test('_dispatchChanged', () => {
-    const eventStub = sandbox.stub(element, 'dispatchEvent');
+    const eventStub = sinon.stub(element, 'dispatchEvent');
     element._dispatchChanged(['new-test-value']);
 
     assert.isTrue(eventStub.called);
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.js
index c1b32da..a73c7cf 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.js
@@ -38,19 +38,14 @@
 suite('gr-plugin-list tests', () => {
   let element;
   let plugins;
-  let sandbox;
+
   let value;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     counter = 0;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('list with plugins', () => {
     setup(done => {
       plugins = _.times(26, pluginGenerator);
@@ -110,10 +105,10 @@
 
   suite('filter', () => {
     test('_paramsChanged', done => {
-      sandbox.stub(
+      sinon.stub(
           element.$.restAPI,
-          'getPlugins',
-          () => Promise.resolve(plugins));
+          'getPlugins')
+          .callsFake(() => Promise.resolve(plugins));
       const value = {
         filter: 'test',
         offset: 25,
@@ -148,7 +143,7 @@
   suite('404', () => {
     test('fires page-error', done => {
       const response = {status: 404};
-      sandbox.stub(element.$.restAPI, 'getPlugins',
+      sinon.stub(element.$.restAPI, 'getPlugins').callsFake(
           (filter, pluginsPerPage, opt_offset, errFn) => {
             errFn(response);
           });
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js
index 6919940..0248dfc 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js
@@ -24,7 +24,7 @@
 
 suite('gr-repo-access tests', () => {
   let element;
-  let sandbox;
+
   let repoStub;
 
   const accessRes = {
@@ -99,37 +99,32 @@
     },
   };
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     stub('gr-rest-api-interface', {
       getAccount() { return Promise.resolve(null); },
     });
-    repoStub = sandbox.stub(element.$.restAPI, 'getRepo').returns(
+    repoStub = sinon.stub(element.$.restAPI, 'getRepo').returns(
         Promise.resolve(repoRes));
     element._loading = false;
     element._ownerOf = [];
     element._canUpload = false;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_repoChanged called when repo name changes', () => {
-    sandbox.stub(element, '_repoChanged');
+    sinon.stub(element, '_repoChanged');
     element.repo = 'New Repo';
     assert.isTrue(element._repoChanged.called);
   });
 
   test('_repoChanged', done => {
-    const accessStub = sandbox.stub(element.$.restAPI,
+    const accessStub = sinon.stub(element.$.restAPI,
         'getRepoAccessRights');
 
     accessStub.withArgs('New Repo').returns(
         Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
     accessStub.withArgs('Another New Repo')
         .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
-    const capabilitiesStub = sandbox.stub(element.$.restAPI,
+    const capabilitiesStub = sinon.stub(element.$.restAPI,
         'getCapabilities');
     capabilitiesStub.returns(Promise.resolve(capabilitiesRes));
 
@@ -164,9 +159,9 @@
         name: 'Access Database',
       },
     };
-    const accessStub = sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
+    const accessStub = sinon.stub(element.$.restAPI, 'getRepoAccessRights')
         .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
-    const capabilitiesStub = sandbox.stub(element.$.restAPI,
+    const capabilitiesStub = sinon.stub(element.$.restAPI,
         'getCapabilities').returns(Promise.resolve(capabilitiesRes));
 
     element._repoChanged().then(() => {
@@ -199,7 +194,7 @@
   test('inherit section', () => {
     element._local = {};
     element._ownerOf = [];
-    sandbox.stub(element, '_computeParentHref');
+    sinon.stub(element, '_computeParentHref');
     // Nothing should appear when no inherit from and not in edit mode.
     assert.equal(getComputedStyle(element.$.inheritsFrom).display, 'none');
     // The autocomplete should be hidden, and the link should be  displayed.
@@ -244,8 +239,9 @@
   test('fires page-error', done => {
     const response = {status: 404};
 
-    sandbox.stub(
-        element.$.restAPI, 'getRepoAccessRights', (repoName, errFn) => {
+    sinon.stub(
+        element.$.restAPI, 'getRepoAccessRights')
+        .callsFake((repoName, errFn) => {
           errFn(response);
         });
 
@@ -352,7 +348,7 @@
     });
 
     test('_handleAccessModified called with event fired', () => {
-      sandbox.spy(element, '_handleAccessModified');
+      sinon.spy(element, '_handleAccessModified');
       element.dispatchEvent(
           new CustomEvent('access-modified', {
             composed: true, bubbles: true,
@@ -370,7 +366,7 @@
             detail: {},
             composed: true, bubbles: true,
           }));
-      sandbox.spy(element, '_handleAccessModified');
+      sinon.spy(element, '_handleAccessModified');
       element.dispatchEvent(
           new CustomEvent('access-modified', {
             detail: {},
@@ -381,8 +377,8 @@
 
     test('_handleSaveForReview', () => {
       const saveStub =
-          sandbox.stub(element.$.restAPI, 'setRepoAccessRightsForReview');
-      sandbox.stub(element, '_computeAddAndRemove').returns({
+          sinon.stub(element.$.restAPI, 'setRepoAccessRightsForReview');
+      sinon.stub(element, '_computeAddAndRemove').returns({
         add: {},
         remove: {},
       });
@@ -1164,16 +1160,16 @@
           },
         },
       };
-      sandbox.stub(element.$.restAPI, 'getRepoAccessRights').returns(
+      sinon.stub(element.$.restAPI, 'getRepoAccessRights').returns(
           Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
-      sandbox.stub(GerritNav, 'navigateToChange');
+      sinon.stub(GerritNav, 'navigateToChange');
       let resolver;
-      const saveStub = sandbox.stub(element.$.restAPI,
+      const saveStub = sinon.stub(element.$.restAPI,
           'setRepoAccessRights')
           .returns(new Promise(r => resolver = r));
 
       element.repo = 'test-repo';
-      sandbox.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
+      sinon.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
 
       element._modified = true;
       MockInteractions.tap(element.$.saveBtn);
@@ -1211,16 +1207,16 @@
           },
         },
       };
-      sandbox.stub(element.$.restAPI, 'getRepoAccessRights').returns(
+      sinon.stub(element.$.restAPI, 'getRepoAccessRights').returns(
           Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
-      sandbox.stub(GerritNav, 'navigateToChange');
+      sinon.stub(GerritNav, 'navigateToChange');
       let resolver;
-      const saveForReviewStub = sandbox.stub(element.$.restAPI,
+      const saveForReviewStub = sinon.stub(element.$.restAPI,
           'setRepoAccessRightsForReview')
           .returns(new Promise(r => resolver = r));
 
       element.repo = 'test-repo';
-      sandbox.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
+      sinon.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
 
       element._modified = true;
       MockInteractions.tap(element.$.saveReviewBtn);
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.js b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.js
index b3fd560..0bb0c55 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.js
@@ -23,31 +23,26 @@
 
 suite('gr-repo-commands tests', () => {
   let element;
-  let sandbox;
+
   let repoStub;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
-    repoStub = sandbox.stub(
+    repoStub = sinon.stub(
         element.$.restAPI,
-        'getProjectConfig',
-        () => Promise.resolve({}));
-  });
-
-  teardown(() => {
-    sandbox.restore();
+        'getProjectConfig')
+        .callsFake(() => Promise.resolve({}));
   });
 
   suite('create new change dialog', () => {
     test('_createNewChange opens modal', () => {
-      const openStub = sandbox.stub(element.$.createChangeOverlay, 'open');
+      const openStub = sinon.stub(element.$.createChangeOverlay, 'open');
       element._createNewChange();
       assert.isTrue(openStub.called);
     });
 
     test('_handleCreateChange called when confirm fired', () => {
-      sandbox.stub(element, '_handleCreateChange');
+      sinon.stub(element, '_handleCreateChange');
       element.$.createChangeDialog.dispatchEvent(
           new CustomEvent('confirm', {
             composed: true, bubbles: true,
@@ -56,7 +51,7 @@
     });
 
     test('_handleCloseCreateChange called when cancel fired', () => {
-      sandbox.stub(element, '_handleCloseCreateChange');
+      sinon.stub(element, '_handleCloseCreateChange');
       element.$.createChangeDialog.dispatchEvent(
           new CustomEvent('cancel', {
             composed: true, bubbles: true,
@@ -72,11 +67,11 @@
     let alertStub;
 
     setup(() => {
-      createChangeStub = sandbox.stub(element.$.restAPI, 'createChange');
-      urlStub = sandbox.stub(GerritNav, 'getEditUrlForDiff');
-      sandbox.stub(GerritNav, 'navigateToRelativeUrl');
-      handleSpy = sandbox.spy(element, '_handleEditRepoConfig');
-      alertStub = sandbox.stub();
+      createChangeStub = sinon.stub(element.$.restAPI, 'createChange');
+      urlStub = sinon.stub(GerritNav, 'getEditUrlForDiff');
+      sinon.stub(GerritNav, 'navigateToRelativeUrl');
+      handleSpy = sinon.spy(element, '_handleEditRepoConfig');
+      alertStub = sinon.stub();
       element.addEventListener('show-alert', alertStub);
     });
 
@@ -121,8 +116,9 @@
       element.repo = 'test';
 
       const response = {status: 404};
-      sandbox.stub(
-          element.$.restAPI, 'getProjectConfig', (repo, errFn) => {
+      sinon.stub(
+          element.$.restAPI, 'getProjectConfig')
+          .callsFake((repo, errFn) => {
             errFn(response);
           });
       element.addEventListener('page-error', e => {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.js
index 0e058be..b4d3575 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.js
@@ -23,20 +23,14 @@
 
 suite('gr-repo-dashboards tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('dashboard table', () => {
     setup(() => {
-      sandbox.stub(element.$.restAPI, 'getRepoDashboards').returns(
+      sinon.stub(element.$.restAPI, 'getRepoDashboards').returns(
           Promise.resolve([
             {
               id: 'default:contributor',
@@ -117,7 +111,7 @@
 
   suite('test url', () => {
     test('_getUrl', () => {
-      sandbox.stub(GerritNav, 'getUrlForRepoDashboard',
+      sinon.stub(GerritNav, 'getUrlForRepoDashboard').callsFake(
           () => '/r/dashboard/test');
 
       assert.equal(element._getUrl('/dashboard/test', {}), '/r/dashboard/test');
@@ -129,8 +123,9 @@
   suite('404', () => {
     test('fires page-error', done => {
       const response = {status: 404};
-      sandbox.stub(
-          element.$.restAPI, 'getRepoDashboards', (repo, errFn) => {
+      sinon.stub(
+          element.$.restAPI, 'getRepoDashboards')
+          .callsFake((repo, errFn) => {
             errFn(response);
           });
 
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js
index fd3c4ae..7190218 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js
@@ -59,18 +59,12 @@
   suite('Branches', () => {
     let element;
     let branches;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       element = basicFixture.instantiate();
       element.detailType = 'branches';
       counter = 0;
-      sandbox.stub(page, 'show');
-    });
-
-    teardown(() => {
-      sandbox.restore();
+      sinon.stub(page, 'show');
     });
 
     suite('list of repo branches', () => {
@@ -122,8 +116,8 @@
       });
 
       test('Edit HEAD button not admin', done => {
-        sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
-        sandbox.stub(element.$.restAPI, 'getRepoAccess').returns(
+        sinon.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
+        sinon.stub(element.$.restAPI, 'getRepoAccess').returns(
             Promise.resolve({
               test: {is_owner: false},
             }));
@@ -146,12 +140,12 @@
         const revisionWithEditing = dom(element.root)
             .querySelector('.revisionWithEditing');
 
-        sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
-        sandbox.stub(element.$.restAPI, 'getRepoAccess').returns(
+        sinon.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
+        sinon.stub(element.$.restAPI, 'getRepoAccess').returns(
             Promise.resolve({
               test: {is_owner: true},
             }));
-        sandbox.stub(element, '_handleSaveRevision');
+        sinon.stub(element, '_handleSaveRevision');
         element._determineIfOwner('test').then(() => {
           assert.equal(element._isOwner, true);
           // The revision container for non-editing enabled row is not visible.
@@ -222,9 +216,9 @@
       });
 
       test('_handleSaveRevision with invalid rev', done => {
-        const event = {model: {set: sandbox.stub()}};
+        const event = {model: {set: sinon.stub()}};
         element._isEditing = true;
-        sandbox.stub(element.$.restAPI, 'setRepoHead').returns(
+        sinon.stub(element.$.restAPI, 'setRepoHead').returns(
             Promise.resolve({
               status: 400,
             })
@@ -238,9 +232,9 @@
       });
 
       test('_handleSaveRevision with valid rev', done => {
-        const event = {model: {set: sandbox.stub()}};
+        const event = {model: {set: sinon.stub()}};
         element._isEditing = true;
-        sandbox.stub(element.$.restAPI, 'setRepoHead').returns(
+        sinon.stub(element.$.restAPI, 'setRepoHead').returns(
             Promise.resolve({
               status: 200,
             })
@@ -284,10 +278,10 @@
 
     suite('filter', () => {
       test('_paramsChanged', done => {
-        sandbox.stub(
+        sinon.stub(
             element.$.restAPI,
-            'getRepoBranches',
-            () => Promise.resolve(branches));
+            'getRepoBranches')
+            .callsFake(() => Promise.resolve(branches));
         const params = {
           detail: 'branches',
           repo: 'test',
@@ -311,7 +305,7 @@
     suite('404', () => {
       test('fires page-error', done => {
         const response = {status: 404};
-        sandbox.stub(element.$.restAPI, 'getRepoBranches',
+        sinon.stub(element.$.restAPI, 'getRepoBranches').callsFake(
             (filter, repo, reposBranchesPerPage, opt_offset, errFn) => {
               errFn(response);
             });
@@ -335,18 +329,12 @@
   suite('Tags', () => {
     let element;
     let tags;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       element = basicFixture.instantiate();
       element.detailType = 'tags';
       counter = 0;
-      sandbox.stub(page, 'show');
-    });
-
-    teardown(() => {
-      sandbox.restore();
+      sinon.stub(page, 'show');
     });
 
     test('_computeMessage', () => {
@@ -468,10 +456,10 @@
 
     suite('filter', () => {
       test('_paramsChanged', done => {
-        sandbox.stub(
+        sinon.stub(
             element.$.restAPI,
-            'getRepoTags',
-            () => Promise.resolve(tags));
+            'getRepoTags')
+            .callsFake(() => Promise.resolve(tags));
         const params = {
           repo: 'test',
           detail: 'tags',
@@ -494,7 +482,7 @@
 
     suite('create new', () => {
       test('_handleCreateClicked called when create-click fired', () => {
-        sandbox.stub(element, '_handleCreateClicked');
+        sinon.stub(element, '_handleCreateClicked');
         element.shadowRoot
             .querySelector('gr-list-view').dispatchEvent(
                 new CustomEvent('create-clicked', {
@@ -504,13 +492,13 @@
       });
 
       test('_handleCreateClicked opens modal', () => {
-        const openStub = sandbox.stub(element.$.createOverlay, 'open');
+        const openStub = sinon.stub(element.$.createOverlay, 'open');
         element._handleCreateClicked();
         assert.isTrue(openStub.called);
       });
 
       test('_handleCreateItem called when confirm fired', () => {
-        sandbox.stub(element, '_handleCreateItem');
+        sinon.stub(element, '_handleCreateItem');
         element.$.createDialog.dispatchEvent(
             new CustomEvent('confirm', {
               composed: true, bubbles: true,
@@ -519,7 +507,7 @@
       });
 
       test('_handleCloseCreate called when cancel fired', () => {
-        sandbox.stub(element, '_handleCloseCreate');
+        sinon.stub(element, '_handleCloseCreate');
         element.$.createDialog.dispatchEvent(
             new CustomEvent('cancel', {
               composed: true, bubbles: true,
@@ -531,7 +519,7 @@
     suite('404', () => {
       test('fires page-error', done => {
         const response = {status: 404};
-        sandbox.stub(element.$.restAPI, 'getRepoTags',
+        sinon.stub(element.$.restAPI, 'getRepoTags').callsFake(
             (filter, repo, reposTagsPerPage, opt_offset, errFn) => {
               errFn(response);
             });
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.js
index e35f8e4..b629cf4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.js
@@ -38,20 +38,15 @@
 suite('gr-repo-list tests', () => {
   let element;
   let repos;
-  let sandbox;
+
   let value;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(page, 'show');
+    sinon.stub(page, 'show');
     element = basicFixture.instantiate();
     counter = 0;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('list with repos', () => {
     setup(done => {
       repos = _.times(26, repoGenerator);
@@ -75,7 +70,7 @@
     });
 
     test('_maybeOpenCreateOverlay', () => {
-      const overlayOpen = sandbox.stub(element.$.createOverlay, 'open');
+      const overlayOpen = sinon.stub(element.$.createOverlay, 'open');
       element._maybeOpenCreateOverlay();
       assert.isFalse(overlayOpen.called);
       const params = {};
@@ -113,7 +108,8 @@
     });
 
     test('_paramsChanged', done => {
-      sandbox.stub(element.$.restAPI, 'getRepos', () => Promise.resolve(repos));
+      sinon.stub(element.$.restAPI, 'getRepos')
+          .callsFake( () => Promise.resolve(repos));
       const value = {
         filter: 'test',
         offset: 25,
@@ -126,7 +122,7 @@
     });
 
     test('latest repos requested are always set', done => {
-      const repoStub = sandbox.stub(element.$.restAPI, 'getRepos');
+      const repoStub = sinon.stub(element.$.restAPI, 'getRepos');
       repoStub.withArgs('test').returns(Promise.resolve(repos));
       repoStub.withArgs('filter').returns(Promise.resolve(reposFiltered));
       element._filter = 'test';
@@ -156,7 +152,7 @@
 
   suite('create new', () => {
     test('_handleCreateClicked called when create-click fired', () => {
-      sandbox.stub(element, '_handleCreateClicked');
+      sinon.stub(element, '_handleCreateClicked');
       element.shadowRoot
           .querySelector('gr-list-view').dispatchEvent(
               new CustomEvent('create-clicked', {
@@ -166,13 +162,13 @@
     });
 
     test('_handleCreateClicked opens modal', () => {
-      const openStub = sandbox.stub(element.$.createOverlay, 'open');
+      const openStub = sinon.stub(element.$.createOverlay, 'open');
       element._handleCreateClicked();
       assert.isTrue(openStub.called);
     });
 
     test('_handleCreateRepo called when confirm fired', () => {
-      sandbox.stub(element, '_handleCreateRepo');
+      sinon.stub(element, '_handleCreateRepo');
       element.$.createDialog.dispatchEvent(
           new CustomEvent('confirm', {
             composed: true, bubbles: true,
@@ -181,7 +177,7 @@
     });
 
     test('_handleCloseCreate called when cancel fired', () => {
-      sandbox.stub(element, '_handleCloseCreate');
+      sinon.stub(element, '_handleCloseCreate');
       element.$.createDialog.dispatchEvent(
           new CustomEvent('cancel', {
             composed: true, bubbles: true,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js
index eea88de..1730839 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js
@@ -22,15 +22,11 @@
 
 suite('gr-repo-plugin-config tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => sandbox.restore());
-
   test('_computePluginConfigOptions', () => {
     assert.deepEqual(element._computePluginConfigOptions(), []);
     assert.deepEqual(element._computePluginConfigOptions({}), []);
@@ -48,7 +44,7 @@
   });
 
   test('_handleChange', () => {
-    const eventStub = sandbox.stub(element, 'dispatchEvent');
+    const eventStub = sinon.stub(element, 'dispatchEvent');
     element.pluginData = {
       name: 'testName',
       config: {plugin: {value: 'test'}},
@@ -72,8 +68,8 @@
     let buildStub;
 
     setup(() => {
-      changeStub = sandbox.stub(element, '_handleChange');
-      buildStub = sandbox.stub(element, '_buildConfigChangeInfo');
+      changeStub = sinon.stub(element, '_handleChange');
+      buildStub = sinon.stub(element, '_buildConfigChangeInfo');
     });
 
     test('ARRAY type option', () => {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.js b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.js
index 345a034..3b42e3b 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.js
@@ -24,7 +24,7 @@
 
 suite('gr-repo tests', () => {
   let element;
-  let sandbox;
+
   let repoStub;
   const repoConf = {
     description: 'Access inherited by all other projects.',
@@ -99,7 +99,6 @@
   }
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(false); },
       getConfig() {
@@ -107,14 +106,10 @@
       },
     });
     element = basicFixture.instantiate();
-    repoStub = sandbox.stub(
+    repoStub = sinon.stub(
         element.$.restAPI,
-        'getProjectConfig',
-        () => Promise.resolve(repoConf));
-  });
-
-  teardown(() => {
-    sandbox.restore();
+        'getProjectConfig')
+        .callsFake(() => Promise.resolve(repoConf));
   });
 
   test('_computePluginData', () => {
@@ -126,7 +121,7 @@
   });
 
   test('_handlePluginConfigChanged', () => {
-    const notifyStub = sandbox.stub(element, 'notifyPath');
+    const notifyStub = sinon.stub(element, 'notifyPath');
     element._repoConfig = {plugin_config: {}};
     element._handlePluginConfigChanged({detail: {
       name: 'test',
@@ -175,11 +170,11 @@
 
   test('form defaults to read only when logged in and not admin', done => {
     element.repo = REPO;
-    sandbox.stub(element, '_getLoggedIn', () => Promise.resolve(true));
-    sandbox.stub(
+    sinon.stub(element, '_getLoggedIn').callsFake(() => Promise.resolve(true));
+    sinon.stub(
         element.$.restAPI,
-        'getRepoAccess',
-        () => Promise.resolve({'test-repo': {}}));
+        'getRepoAccess')
+        .callsFake(() => Promise.resolve({'test-repo': {}}));
     element._loadRepo().then(() => {
       assert.isTrue(element._readOnly);
       done();
@@ -252,10 +247,10 @@
     element.repo = 'test';
 
     const response = {status: 404};
-    sandbox.stub(
-        element.$.restAPI, 'getProjectConfig', (repo, errFn) => {
-          errFn(response);
-        });
+    sinon.stub(
+        element.$.restAPI, 'getProjectConfig').callsFake((repo, errFn) => {
+      errFn(response);
+    });
     element.addEventListener('page-error', e => {
       assert.deepEqual(e.detail.response, response);
       done();
@@ -267,11 +262,12 @@
   suite('admin', () => {
     setup(() => {
       element.repo = REPO;
-      sandbox.stub(element, '_getLoggedIn', () => Promise.resolve(true));
-      sandbox.stub(
+      sinon.stub(element, '_getLoggedIn')
+          .callsFake(() => Promise.resolve(true));
+      sinon.stub(
           element.$.restAPI,
-          'getRepoAccess',
-          () => Promise.resolve({'test-repo': {is_owner: true}}));
+          'getRepoAccess')
+          .callsFake(() => Promise.resolve({'test-repo': {is_owner: true}}));
     });
 
     test('all form elements are enabled', done => {
@@ -327,8 +323,8 @@
         enable_reviewer_by_email: 'TRUE',
       };
 
-      const saveStub = sandbox.stub(element.$.restAPI, 'saveRepoConfig'
-          , () => Promise.resolve({}));
+      const saveStub = sinon.stub(element.$.restAPI, 'saveRepoConfig')
+          .callsFake(() => Promise.resolve({}));
 
       const button = dom(element.root).querySelector('gr-button');
 
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.js
index 40cee04..9364a50 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.js
@@ -23,17 +23,11 @@
 
 suite('gr-rule-editor tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('unit tests', () => {
     test('_computeForce, _computeForceClass, and _computeForceOptions',
         () => {
@@ -133,7 +127,7 @@
     test('_setDefaultRuleValues', () => {
       element.rule = {id: 123};
       const defaultValue = {action: 'ALLOW'};
-      sandbox.stub(element, '_getDefaultRuleValues').returns(defaultValue);
+      sinon.stub(element, '_getDefaultRuleValues').returns(defaultValue);
       element._setDefaultRuleValues();
       assert.isTrue(element._getDefaultRuleValues.called);
       assert.equal(element.rule.value, defaultValue);
@@ -156,7 +150,7 @@
     });
 
     test('_handleValueChange', () => {
-      const modifiedHandler = sandbox.stub();
+      const modifiedHandler = sinon.stub();
       element.rule = {value: {}};
       element.addEventListener('access-modified', modifiedHandler);
       element._handleValueChange();
@@ -346,7 +340,7 @@
 
     test('remove value', () => {
       element.editing = true;
-      const removeStub = sandbox.stub();
+      const removeStub = sinon.stub();
       element.addEventListener('added-rule-removed', removeStub);
       MockInteractions.tap(element.$.removeBtn);
       flushAsynchronousOperations();
@@ -399,7 +393,7 @@
     });
 
     test('modify value', () => {
-      const removeStub = sandbox.stub();
+      const removeStub = sinon.stub();
       element.addEventListener('added-rule-removed', removeStub);
       assert.isNotOk(element.rule.value.modified);
       dom(element.root).querySelector('#labelMin').bindValue = 1;
@@ -414,7 +408,7 @@
 
   suite('new rule with labels', () => {
     setup(done => {
-      sandbox.spy(element, '_setDefaultRuleValues');
+      sinon.spy(element, '_setDefaultRuleValues');
       element.label = {values: [
         {value: -2, text: 'This shall not be merged'},
         {value: -1, text: 'I would prefer this is not merged as is'},
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.js
index 5084a3a..4b9424b 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.js
@@ -23,10 +23,8 @@
 
 suite('gr-change-list-item tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
       getLoggedIn() { return Promise.resolve(false); },
@@ -34,8 +32,6 @@
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('computed fields', () => {
     assert.equal(element._computeLabelClass({labels: {}}),
         'cell label u-gray-background');
@@ -261,7 +257,7 @@
   });
 
   test('change params passed to gr-navigation', () => {
-    sandbox.stub(GerritNav);
+    sinon.stub(GerritNav);
     const change = {
       internalHost: 'test-host',
       project: 'test-repo',
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.js
index 89815f3..f945476 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.js
@@ -27,7 +27,6 @@
 
 suite('gr-change-list-view tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     stub('gr-rest-api-interface', {
@@ -39,12 +38,10 @@
       getAccountStatus() { return Promise.resolve({}); },
     });
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
   });
 
   teardown(done => {
     flush(() => {
-      sandbox.restore();
       done();
     });
   });
@@ -64,7 +61,7 @@
   });
 
   test('_computeNavLink', () => {
-    const getUrlStub = sandbox.stub(GerritNav, 'getUrlForSearchQuery')
+    const getUrlStub = sinon.stub(GerritNav, 'getUrlForSearchQuery')
         .returns('');
     const query = 'status:open';
     let offset = 0;
@@ -110,7 +107,7 @@
   });
 
   test('_handleNextPage', () => {
-    const showStub = sandbox.stub(page, 'show');
+    const showStub = sinon.stub(page, 'show');
     element.$.nextArrow.hidden = true;
     element._handleNextPage();
     assert.isFalse(showStub.called);
@@ -120,7 +117,7 @@
   });
 
   test('_handlePreviousPage', () => {
-    const showStub = sandbox.stub(page, 'show');
+    const showStub = sinon.stub(page, 'show');
     element.$.prevArrow.hidden = true;
     element._handlePreviousPage();
     assert.isFalse(showStub.called);
@@ -186,16 +183,16 @@
 
     teardown(done => {
       flush(() => {
-        sandbox.restore();
+        sinon.restore();
         done();
       });
     });
 
     test('Searching for a change ID redirects to change', done => {
       const change = {_number: 1};
-      sandbox.stub(element, '_getChanges')
+      sinon.stub(element, '_getChanges')
           .returns(Promise.resolve([change]));
-      sandbox.stub(GerritNav, 'navigateToChange', url => {
+      sinon.stub(GerritNav, 'navigateToChange').callsFake( url => {
         assert.equal(url, change);
         done();
       });
@@ -205,9 +202,9 @@
 
     test('Searching for a change num redirects to change', done => {
       const change = {_number: 1};
-      sandbox.stub(element, '_getChanges')
+      sinon.stub(element, '_getChanges')
           .returns(Promise.resolve([change]));
-      sandbox.stub(GerritNav, 'navigateToChange', url => {
+      sinon.stub(GerritNav, 'navigateToChange').callsFake( url => {
         assert.equal(url, change);
         done();
       });
@@ -217,9 +214,9 @@
 
     test('Commit hash redirects to change', done => {
       const change = {_number: 1};
-      sandbox.stub(element, '_getChanges')
+      sinon.stub(element, '_getChanges')
           .returns(Promise.resolve([change]));
-      sandbox.stub(GerritNav, 'navigateToChange', url => {
+      sinon.stub(GerritNav, 'navigateToChange').callsFake( url => {
         assert.equal(url, change);
         done();
       });
@@ -228,9 +225,9 @@
     });
 
     test('Searching for an invalid change ID searches', () => {
-      sandbox.stub(element, '_getChanges')
+      sinon.stub(element, '_getChanges')
           .returns(Promise.resolve([]));
-      const stub = sandbox.stub(GerritNav, 'navigateToChange');
+      const stub = sinon.stub(GerritNav, 'navigateToChange');
 
       element.params = {view: GerritNav.View.SEARCH, query: CHANGE_ID};
       flushAsynchronousOperations();
@@ -239,9 +236,9 @@
     });
 
     test('Change ID with multiple search results searches', () => {
-      sandbox.stub(element, '_getChanges')
+      sinon.stub(element, '_getChanges')
           .returns(Promise.resolve([{}, {}]));
-      const stub = sandbox.stub(GerritNav, 'navigateToChange');
+      const stub = sinon.stub(GerritNav, 'navigateToChange');
 
       element.params = {view: GerritNav.View.SEARCH, query: CHANGE_ID};
       flushAsynchronousOperations();
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js
index 5cf5d3b..78973df 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js
@@ -26,7 +26,6 @@
 
 suite('gr-change-list basic tests', () => {
   let element;
-  let sandbox;
 
   suiteSetup(() => {
     const kb = TestKeyboardShortcutBinder.push();
@@ -45,12 +44,9 @@
   });
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   suite('test show change number not logged in', () => {
     setup(() => {
       element = basicFixture.instantiate();
@@ -153,7 +149,7 @@
   });
 
   test('keyboard shortcuts', done => {
-    sandbox.stub(element, '_computeLabelNames');
+    sinon.stub(element, '_computeLabelNames');
     element.sections = [
       {results: new Array(1)},
       {results: new Array(2)},
@@ -178,7 +174,7 @@
       assert.equal(element.selectedIndex, 2);
       assert.isTrue(elementItems[2].hasAttribute('selected'));
 
-      const navStub = sandbox.stub(GerritNav, 'navigateToChange');
+      const navStub = sinon.stub(GerritNav, 'navigateToChange');
       assert.equal(element.selectedIndex, 2);
       MockInteractions.pressAndReleaseKeyOn(element, 13, null, 'enter');
       assert.deepEqual(navStub.lastCall.args[0], {_number: 2},
@@ -195,7 +191,7 @@
       MockInteractions.pressAndReleaseKeyOn(element, 75, null, 'k');
       assert.equal(element.selectedIndex, 0);
 
-      const reloadStub = sandbox.stub(element, '_reloadWindow');
+      const reloadStub = sinon.stub(element, '_reloadWindow');
       MockInteractions.pressAndReleaseKeyOn(element, 82, 'shift', 'r');
       assert.isTrue(reloadStub.called);
 
@@ -457,14 +453,12 @@
 
   suite('dashboard queries', () => {
     let element;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       element = basicFixture.instantiate();
     });
 
-    teardown(() => { sandbox.restore(); });
+    teardown(() => { sinon.restore(); });
 
     test('query without age and limit unchanged', () => {
       const query = 'status:closed owner:me';
@@ -510,15 +504,11 @@
 
   suite('gr-change-list sections', () => {
     let element;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       element = basicFixture.instantiate();
     });
 
-    teardown(() => { sandbox.restore(); });
-
     test('keyboard shortcuts', done => {
       element.selectedIndex = 0;
       element.sections = [
@@ -554,7 +544,7 @@
         assert.equal(element.selectedIndex, 1);
         MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
 
-        const navStub = sandbox.stub(GerritNav, 'navigateToChange');
+        const navStub = sinon.stub(GerritNav, 'navigateToChange');
         assert.equal(element.selectedIndex, 2);
 
         MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
@@ -623,7 +613,7 @@
     });
 
     test('_computeItemAbsoluteIndex', () => {
-      sandbox.stub(element, '_computeLabelNames');
+      sinon.stub(element, '_computeLabelNames');
       element.sections = [
         {results: new Array(1)},
         {results: new Array(2)},
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js
index bf24c1b..bdd374a 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js
@@ -24,7 +24,7 @@
 
 suite('gr-dashboard-view tests', () => {
   let element;
-  let sandbox;
+
   let paramsChangedPromise;
   let getChangesStub;
 
@@ -35,8 +35,8 @@
       getAccountStatus() { return Promise.resolve(false); },
     });
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-    getChangesStub = sandbox.stub(element.$.restAPI, 'getChanges',
+
+    getChangesStub = sinon.stub(element.$.restAPI, 'getChanges').callsFake(
         (_, qs) => Promise.resolve(qs.map(() => [])));
 
     let resolver;
@@ -44,15 +44,11 @@
       resolver = resolve;
     });
     const paramsChanged = element._paramsChanged.bind(element);
-    sandbox.stub(element, '_paramsChanged', params => {
+    sinon.stub(element, '_paramsChanged').callsFake( params => {
       paramsChanged(params).then(() => resolver());
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('drafts banner functionality', () => {
     suite('_maybeShowDraftsBanner', () => {
       test('not dashboard/self', () => {
@@ -71,7 +67,7 @@
       test('no drafts on open changes', () => {
         element.params = {user: 'self'};
         element._results = [{query: 'has:draft', results: [{status: '_'}]}];
-        sandbox.stub(element, 'changeIsOpen').returns(true);
+        sinon.stub(element, 'changeIsOpen').returns(true);
         element._maybeShowDraftsBanner();
         assert.isFalse(element._showDraftsBanner);
       });
@@ -79,7 +75,7 @@
       test('no drafts on open changes', () => {
         element.params = {user: 'self'};
         element._results = [{query: 'has:draft', results: [{status: '_'}]}];
-        sandbox.stub(element, 'changeIsOpen').returns(false);
+        sinon.stub(element, 'changeIsOpen').returns(false);
         element._maybeShowDraftsBanner();
         assert.isTrue(element._showDraftsBanner);
       });
@@ -98,7 +94,7 @@
     });
 
     test('delete tap opens dialog', () => {
-      sandbox.stub(element, '_handleOpenDeleteDialog');
+      sinon.stub(element, '_handleOpenDeleteDialog');
       element._showDraftsBanner = true;
       flushAsynchronousOperations();
 
@@ -108,15 +104,15 @@
     });
 
     test('delete comments flow', async () => {
-      sandbox.spy(element, '_handleConfirmDelete');
-      sandbox.stub(element, '_reload');
+      sinon.spy(element, '_handleConfirmDelete');
+      sinon.stub(element, '_reload');
 
       // Set up control over timing of when RPC resolves.
       let deleteDraftCommentsPromiseResolver;
       const deleteDraftCommentsPromise = new Promise(resolve => {
         deleteDraftCommentsPromiseResolver = resolve;
       });
-      sandbox.stub(element.$.restAPI, 'deleteDraftComments')
+      sinon.stub(element.$.restAPI, 'deleteDraftComments')
           .returns(deleteDraftCommentsPromise);
 
       // Open confirmation dialog and tap confirm button.
@@ -231,14 +227,15 @@
 
   suite('_getProjectDashboard', () => {
     test('dashboard with foreach', () => {
-      sandbox.stub(element.$.restAPI, 'getDashboard', () => Promise.resolve({
-        title: 'title',
-        foreach: 'foreach for ${project}',
-        sections: [
-          {name: 'section 1', query: 'query 1'},
-          {name: 'section 2', query: '${project} query 2'},
-        ],
-      }));
+      sinon.stub(element.$.restAPI, 'getDashboard')
+          .callsFake( () => Promise.resolve({
+            title: 'title',
+            foreach: 'foreach for ${project}',
+            sections: [
+              {name: 'section 1', query: 'query 1'},
+              {name: 'section 2', query: '${project} query 2'},
+            ],
+          }));
       return element._getProjectDashboard('project', '').then(dashboard => {
         assert.deepEqual(
             dashboard,
@@ -256,13 +253,14 @@
     });
 
     test('dashboard without foreach', () => {
-      sandbox.stub(element.$.restAPI, 'getDashboard', () => Promise.resolve({
-        title: 'title',
-        sections: [
-          {name: 'section 1', query: 'query 1'},
-          {name: 'section 2', query: '${project} query 2'},
-        ],
-      }));
+      sinon.stub(element.$.restAPI, 'getDashboard').callsFake(
+          () => Promise.resolve({
+            title: 'title',
+            sections: [
+              {name: 'section 1', query: 'query 1'},
+              {name: 'section 2', query: '${project} query 2'},
+            ],
+          }));
       return element._getProjectDashboard('project', '').then(dashboard => {
         assert.deepEqual(
             dashboard,
@@ -283,7 +281,7 @@
       {name: 'test2', query: 'test2', hideIfEmpty: true},
     ];
     getChangesStub.restore();
-    sandbox.stub(element.$.restAPI, 'getChanges')
+    sinon.stub(element.$.restAPI, 'getChanges')
         .returns(Promise.resolve([[], ['nonempty']]));
 
     return element._fetchDashboardChanges({sections}, false).then(() => {
@@ -298,7 +296,7 @@
       {name: 'test2', query: 'test2'},
     ];
     getChangesStub.restore();
-    sandbox.stub(element.$.restAPI, 'getChanges')
+    sinon.stub(element.$.restAPI, 'getChanges')
         .returns(Promise.resolve([[], []]));
 
     return element._fetchDashboardChanges({sections}, false).then(() => {
@@ -336,7 +334,7 @@
 
   test('404 page', done => {
     const response = {status: 404};
-    sandbox.stub(element.$.restAPI, 'getDashboard',
+    sinon.stub(element.$.restAPI, 'getDashboard').callsFake(
         async (project, dashboard, errFn) => {
           errFn(response);
         });
@@ -352,7 +350,7 @@
   });
 
   test('params change triggers dashboardDisplayed()', () => {
-    sandbox.stub(element.reporting, 'dashboardDisplayed');
+    sinon.stub(element.reporting, 'dashboardDisplayed');
     element.params = {
       view: GerritNav.View.DASHBOARD,
       project: 'project',
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.js b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.js
index b93bbb2..4f93d54 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.js
@@ -23,17 +23,13 @@
 
 suite('gr-repo-header tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('repoUrl reset once repo changed', () => {
-    sandbox.stub(GerritNav, 'getUrlForRepo',
+    sinon.stub(GerritNav, 'getUrlForRepo').callsFake(
         repoName => `http://test.com/${repoName}`
     );
     assert.equal(element._repoUrl, undefined);
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.js b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.js
index 76272c8..6baacef 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.js
@@ -22,23 +22,19 @@
 
 suite('gr-user-header tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('loads and clears account info', done => {
-    sandbox.stub(element.$.restAPI, 'getAccountDetails')
+    sinon.stub(element.$.restAPI, 'getAccountDetails')
         .returns(Promise.resolve({
           name: 'foo',
           email: 'bar',
           registered_on: '2015-03-12 18:32:08.000000000',
         }));
-    sandbox.stub(element.$.restAPI, 'getAccountStatus')
+    sinon.stub(element.$.restAPI, 'getAccountStatus')
         .returns(Promise.resolve('baz'));
 
     element.userId = 'foo.bar@baz';
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js
index daa3139..2702af4 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js
@@ -30,7 +30,6 @@
 // TODO(dhruvsri): remove use of _populateRevertMessage as it's private
 suite('gr-change-actions tests', () => {
   let element;
-  let sandbox;
 
   suite('basic tests', () => {
     setup(() => {
@@ -85,8 +84,7 @@
         getProjectConfig() { return Promise.resolve({}); },
       });
 
-      sandbox = sinon.sandbox.create();
-      sandbox.stub(pluginLoader, 'awaitPluginsLoaded')
+      sinon.stub(pluginLoader, 'awaitPluginsLoaded')
           .returns(Promise.resolve());
 
       element = basicFixture.instantiate();
@@ -101,18 +99,14 @@
           enabled: true,
         },
       };
-      sandbox.stub(element.$.confirmCherrypick.$.restAPI,
+      sinon.stub(element.$.confirmCherrypick.$.restAPI,
           'getRepoBranches').returns(Promise.resolve([]));
-      sandbox.stub(element.$.confirmMove.$.restAPI,
+      sinon.stub(element.$.confirmMove.$.restAPI,
           'getRepoBranches').returns(Promise.resolve([]));
 
       return element.reload();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('show-revision-actions event should fire', done => {
       const spy = sinon.spy(element, '_sendShowRevisionActions');
       element.reload();
@@ -144,7 +138,7 @@
     });
 
     test('plugin revision actions', done => {
-      sandbox.stub(element.$.restAPI, 'getChangeActionURL').returns(
+      sinon.stub(element.$.restAPI, 'getChangeActionURL').returns(
           Promise.resolve('the-url'));
       element.revisionActions = {
         'plugin~action': {},
@@ -159,7 +153,7 @@
     });
 
     test('plugin change actions', done => {
-      sandbox.stub(element.$.restAPI, 'getChangeActionURL').returns(
+      sinon.stub(element.$.restAPI, 'getChangeActionURL').returns(
           Promise.resolve('the-url'));
       element.actions = {
         'plugin~action': {},
@@ -274,12 +268,12 @@
     });
 
     test('submit change', () => {
-      const showSpy = sandbox.spy(element, '_showActionDialog');
-      sandbox.stub(element.$.restAPI, 'getFromProjectLookup')
+      const showSpy = sinon.spy(element, '_showActionDialog');
+      sinon.stub(element.$.restAPI, 'getFromProjectLookup')
           .returns(Promise.resolve('test'));
-      sandbox.stub(element, 'fetchChangeUpdates',
+      sinon.stub(element, 'fetchChangeUpdates').callsFake(
           () => Promise.resolve({isLatest: true}));
-      sandbox.stub(element.$.overlay, 'open').returns(Promise.resolve());
+      sinon.stub(element.$.overlay, 'open').returns(Promise.resolve());
       element.change = {
         revisions: {
           rev1: {_number: 1},
@@ -298,12 +292,12 @@
     });
 
     test('submit change, tap on icon', done => {
-      sandbox.stub(element.$.confirmSubmitDialog, 'resetFocus', done);
-      sandbox.stub(element.$.restAPI, 'getFromProjectLookup')
+      sinon.stub(element.$.confirmSubmitDialog, 'resetFocus').callsFake( done);
+      sinon.stub(element.$.restAPI, 'getFromProjectLookup')
           .returns(Promise.resolve('test'));
-      sandbox.stub(element, 'fetchChangeUpdates',
+      sinon.stub(element, 'fetchChangeUpdates').callsFake(
           () => Promise.resolve({isLatest: true}));
-      sandbox.stub(element.$.overlay, 'open').returns(Promise.resolve());
+      sinon.stub(element.$.overlay, 'open').returns(Promise.resolve());
       element.change = {
         revisions: {
           rev1: {_number: 1},
@@ -320,8 +314,8 @@
     });
 
     test('_handleSubmitConfirm', () => {
-      const fireStub = sandbox.stub(element, '_fireAction');
-      sandbox.stub(element, '_canSubmitChange').returns(true);
+      const fireStub = sinon.stub(element, '_fireAction');
+      sinon.stub(element, '_canSubmitChange').returns(true);
       element._handleSubmitConfirm();
       assert.isTrue(fireStub.calledOnce);
       assert.deepEqual(fireStub.lastCall.args,
@@ -329,16 +323,16 @@
     });
 
     test('_handleSubmitConfirm when not able to submit', () => {
-      const fireStub = sandbox.stub(element, '_fireAction');
-      sandbox.stub(element, '_canSubmitChange').returns(false);
+      const fireStub = sinon.stub(element, '_fireAction');
+      sinon.stub(element, '_canSubmitChange').returns(false);
       element._handleSubmitConfirm();
       assert.isFalse(fireStub.called);
     });
 
     test('submit change with plugin hook', done => {
-      sandbox.stub(element, '_canSubmitChange',
+      sinon.stub(element, '_canSubmitChange').callsFake(
           () => false);
-      const fireActionStub = sandbox.stub(element, '_fireAction');
+      const fireActionStub = sinon.stub(element, '_fireAction');
       flush(() => {
         const submitButton = element.shadowRoot
             .querySelector('gr-button[data-action-key="submit"]');
@@ -378,8 +372,8 @@
     });
 
     test('rebase change', done => {
-      const fireActionStub = sandbox.stub(element, '_fireAction');
-      const fetchChangesStub = sandbox.stub(element.$.confirmRebase,
+      const fireActionStub = sinon.stub(element, '_fireAction');
+      const fetchChangesStub = sinon.stub(element.$.confirmRebase,
           'fetchRecentChanges').returns(Promise.resolve([]));
       element._hasKnownChainState = true;
       flush(() => {
@@ -404,8 +398,8 @@
     });
 
     test('rebase change calls navigateToChange', done => {
-      const navigateToChangeStub = sandbox.stub(GerritNav, 'navigateToChange');
-      sandbox.stub(element.$.restAPI, 'getResponseObject').returns(
+      const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
+      sinon.stub(element.$.restAPI, 'getResponseObject').returns(
           Promise.resolve({}));
       element._handleResponse({__key: 'rebase'}, {});
       flush(() => {
@@ -415,7 +409,7 @@
     });
 
     test(`rebase dialog gets recent changes each time it's opened`, done => {
-      const fetchChangesStub = sandbox.stub(element.$.confirmRebase,
+      const fetchChangesStub = sinon.stub(element.$.confirmRebase,
           'fetchRecentChanges').returns(Promise.resolve([]));
       element._hasKnownChainState = true;
       const rebaseButton = element.shadowRoot
@@ -443,7 +437,7 @@
         MockInteractions.tap(rebaseButton);
         flushAsynchronousOperations();
         assert.isFalse(element.$.confirmRebase.hidden);
-        sandbox.stub(element.$.restAPI, 'getChanges')
+        sinon.stub(element.$.restAPI, 'getChanges')
             .returns(Promise.resolve([]));
         element._handleCherrypickTap();
         flush(() => {
@@ -455,7 +449,7 @@
     });
 
     test('fullscreen-overlay-opened hides content', () => {
-      sandbox.spy(element, '_handleHideBackgroundContent');
+      sinon.spy(element, '_handleHideBackgroundContent');
       element.$.overlay.dispatchEvent(
           new CustomEvent('fullscreen-overlay-opened', {
             composed: true, bubbles: true,
@@ -465,7 +459,7 @@
     });
 
     test('fullscreen-overlay-closed shows content', () => {
-      sandbox.spy(element, '_handleShowBackgroundContent');
+      sinon.spy(element, '_handleShowBackgroundContent');
       element.$.overlay.dispatchEvent(
           new CustomEvent('fullscreen-overlay-closed', {
             composed: true, bubbles: true,
@@ -477,8 +471,8 @@
     test('_setLabelValuesOnRevert', () => {
       const labels = {'Foo': 1, 'Bar-Baz': -2};
       const changeId = 1234;
-      sandbox.stub(element.$.jsAPI, 'getLabelValuesPostRevert').returns(labels);
-      const saveStub = sandbox.stub(element.$.restAPI, 'saveChangeReview')
+      sinon.stub(element.$.jsAPI, 'getLabelValuesPostRevert').returns(labels);
+      const saveStub = sinon.stub(element.$.restAPI, 'saveChangeReview')
           .returns(Promise.resolve());
       return element._setLabelValuesOnRevert(changeId).then(() => {
         assert.isTrue(saveStub.calledOnce);
@@ -511,7 +505,7 @@
         element.set('editMode', true);
         element.set('editPatchsetLoaded', true);
 
-        const fireActionStub = sandbox.stub(element, '_fireAction');
+        const fireActionStub = sinon.stub(element, '_fireAction');
         element._handleDeleteEditTap();
         assert.isFalse(element.$.confirmDeleteEditDialog.hidden);
         MockInteractions.tap(
@@ -644,8 +638,8 @@
       let fireActionStub;
 
       setup(() => {
-        fireActionStub = sandbox.stub(element, '_fireAction');
-        sandbox.stub(window, 'alert');
+        fireActionStub = sinon.stub(element, '_fireAction');
+        sinon.stub(window, 'alert');
       });
 
       test('works', () => {
@@ -753,7 +747,7 @@
           },
         ];
         setup(done => {
-          sandbox.stub(element.$.restAPI, 'getChanges')
+          sinon.stub(element.$.restAPI, 'getChanges')
               .returns(Promise.resolve(changes));
           element._handleCherrypickTap();
           flush(() => {
@@ -818,8 +812,8 @@
       let fireActionStub;
 
       setup(() => {
-        fireActionStub = sandbox.stub(element, '_fireAction');
-        sandbox.stub(window, 'alert');
+        fireActionStub = sinon.stub(element, '_fireAction');
+        sinon.stub(window, 'alert');
       });
 
       test('works', () => {
@@ -900,8 +894,8 @@
       let fireActionStub;
 
       setup(() => {
-        fireActionStub = sandbox.stub(element, '_fireAction');
-        alertStub = sandbox.stub(window, 'alert');
+        fireActionStub = sinon.stub(element, '_fireAction');
+        alertStub = sinon.stub(window, 'alert');
         element.actions = {
           abandon: {
             method: 'POST',
@@ -970,7 +964,7 @@
       let fireActionStub;
 
       setup(() => {
-        fireActionStub = sandbox.stub(element, '_fireAction');
+        fireActionStub = sinon.stub(element, '_fireAction');
         element.commitMessage = 'random commit message';
         element.change.current_revision = 'abcdef';
         element.actions = {
@@ -986,18 +980,18 @@
 
       test('revert change with plugin hook', done => {
         const newRevertMsg = 'Modified revert msg';
-        sandbox.stub(element.$.confirmRevertDialog, '_modifyRevertMsg',
+        sinon.stub(element.$.confirmRevertDialog, '_modifyRevertMsg').callsFake(
             () => newRevertMsg);
         element.change = {
           current_revision: 'abc1234',
         };
-        sandbox.stub(element.$.restAPI, 'getChanges')
+        sinon.stub(element.$.restAPI, 'getChanges')
             .returns(Promise.resolve([
               {change_id: '12345678901234', topic: 'T', subject: 'random'},
               {change_id: '23456', topic: 'T', subject: 'a'.repeat(100)},
             ]));
-        sandbox.stub(element.$.confirmRevertDialog,
-            '_populateRevertSubmissionMessage', () => 'original msg');
+        sinon.stub(element.$.confirmRevertDialog,
+            '_populateRevertSubmissionMessage').callsFake(() => 'original msg');
         flush(() => {
           const revertButton = element.shadowRoot
               .querySelector('gr-button[data-action-key="revert"]');
@@ -1016,7 +1010,7 @@
             submission_id: '199 0',
             current_revision: '2000',
           };
-          getChangesStub = sandbox.stub(element.$.restAPI, 'getChanges')
+          getChangesStub = sinon.stub(element.$.restAPI, 'getChanges')
               .returns(Promise.resolve([
                 {change_id: '12345678901234', topic: 'T', subject: 'random'},
                 {change_id: '23456', topic: 'T', subject: 'a'.repeat(100)},
@@ -1063,7 +1057,7 @@
               .querySelector('gr-button[data-action-key="revert"]');
           const confirmRevertDialog = element.$.confirmRevertDialog;
           MockInteractions.tap(revertButton);
-          const fireStub = sandbox.stub(confirmRevertDialog, 'dispatchEvent');
+          const fireStub = sinon.stub(confirmRevertDialog, 'dispatchEvent');
           flush(() => {
             const confirmButton = element.$.confirmRevertDialog.shadowRoot
                 .querySelector('gr-dialog')
@@ -1126,7 +1120,7 @@
             submission_id: '199',
             current_revision: '2000',
           };
-          sandbox.stub(element.$.restAPI, 'getChanges')
+          sinon.stub(element.$.restAPI, 'getChanges')
               .returns(Promise.resolve([
                 {change_id: '12345678901234', topic: 'T', subject: 'random'},
               ]));
@@ -1137,7 +1131,7 @@
               .querySelector('gr-button[data-action-key="revert"]');
           const confirmRevertDialog = element.$.confirmRevertDialog;
           MockInteractions.tap(revertButton);
-          const fireStub = sandbox.stub(confirmRevertDialog, 'dispatchEvent');
+          const fireStub = sinon.stub(confirmRevertDialog, 'dispatchEvent');
           flush(() => {
             const confirmButton = element.$.confirmRevertDialog.shadowRoot
                 .querySelector('gr-dialog')
@@ -1289,7 +1283,7 @@
       let deleteAction;
 
       setup(() => {
-        fireActionStub = sandbox.stub(element, '_fireAction');
+        fireActionStub = sinon.stub(element, '_fireAction');
         element.change = {
           current_revision: 'abc1234',
         };
@@ -1338,7 +1332,7 @@
 
     suite('ignore change', () => {
       setup(done => {
-        sandbox.stub(element, '_fireAction');
+        sinon.stub(element, '_fireAction');
 
         const IgnoreAction = {
           __key: 'ignore',
@@ -1381,7 +1375,7 @@
 
     suite('unignore change', () => {
       setup(done => {
-        sandbox.stub(element, '_fireAction');
+        sinon.stub(element, '_fireAction');
 
         const UnignoreAction = {
           __key: 'unignore',
@@ -1424,7 +1418,7 @@
 
     suite('reviewed change', () => {
       setup(done => {
-        sandbox.stub(element, '_fireAction');
+        sinon.stub(element, '_fireAction');
 
         const ReviewedAction = {
           __key: 'reviewed',
@@ -1481,7 +1475,7 @@
 
     suite('unreviewed change', () => {
       setup(done => {
-        sandbox.stub(element, '_fireAction');
+        sinon.stub(element, '_fireAction');
 
         const UnreviewedAction = {
           __key: 'unreviewed',
@@ -1613,7 +1607,7 @@
       });
 
       test('approves when tapped', () => {
-        const fireActionStub = sandbox.stub(element, '_fireAction');
+        const fireActionStub = sinon.stub(element, '_fireAction');
         MockInteractions.tap(
             element.shadowRoot
                 .querySelector('gr-button[data-action-key=\'review\']'));
@@ -1717,7 +1711,7 @@
     });
 
     test('adds download revision action', () => {
-      const handler = sandbox.stub();
+      const handler = sinon.stub();
       element.addEventListener('download-tap', handler);
       assert.ok(element.revisionActions.download);
       element._handleDownloadTap();
@@ -1727,7 +1721,7 @@
     });
 
     test('changing changeNum or patchNum does not reload', () => {
-      const reloadStub = sandbox.stub(element, 'reload');
+      const reloadStub = sinon.stub(element, 'reload');
       element.changeNum = 123;
       assert.isFalse(reloadStub.called);
       element.latestPatchNum = 456;
@@ -1769,7 +1763,7 @@
 
       suite('_waitForChangeReachable', () => {
         setup(() => {
-          sandbox.stub(element, 'async', fn => fn());
+          sinon.stub(element, 'async').callsFake( fn => fn());
         });
 
         const makeGetChange = numTries => () => {
@@ -1782,14 +1776,16 @@
         };
 
         test('succeed', () => {
-          sandbox.stub(element.$.restAPI, 'getChange', makeGetChange(5));
+          sinon.stub(element.$.restAPI, 'getChange')
+              .callsFake( makeGetChange(5));
           return element._waitForChangeReachable(123).then(success => {
             assert.isTrue(success);
           });
         });
 
         test('fail', () => {
-          sandbox.stub(element.$.restAPI, 'getChange', makeGetChange(6));
+          sinon.stub(element.$.restAPI, 'getChange')
+              .callsFake( makeGetChange(6));
           return element._waitForChangeReachable(123).then(success => {
             assert.isFalse(success);
           });
@@ -1820,15 +1816,15 @@
       suite('happy path', () => {
         let sendStub;
         setup(() => {
-          sandbox.stub(element, 'fetchChangeUpdates')
+          sinon.stub(element, 'fetchChangeUpdates')
               .returns(Promise.resolve({isLatest: true}));
-          sendStub = sandbox.stub(element.$.restAPI, 'executeChangeAction')
+          sendStub = sinon.stub(element.$.restAPI, 'executeChangeAction')
               .returns(Promise.resolve({}));
-          getResponseObjectStub = sandbox.stub(element.$.restAPI,
+          getResponseObjectStub = sinon.stub(element.$.restAPI,
               'getResponseObject');
-          sandbox.stub(GerritNav,
+          sinon.stub(GerritNav,
               'navigateToChange').returns(Promise.resolve(true));
-          sandbox.stub(element, 'computeLatestPatchNum')
+          sinon.stub(element, 'computeLatestPatchNum')
               .returns(element.latestPatchNum);
         });
 
@@ -1848,7 +1844,7 @@
           setup(() => {
             element.change.submission_id = '199';
             element.change.current_revision = '2000';
-            sandbox.stub(element.$.restAPI, 'getChanges')
+            sinon.stub(element.$.restAPI, 'getChanges')
                 .returns(Promise.resolve([
                   {change_id: '12345678901234', topic: 'T', subject: 'random'},
                   {change_id: '23456', topic: 'T', subject: 'a'.repeat(100)},
@@ -1863,7 +1859,7 @@
               '23456: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...' +
               '\n';
             const modifiedMsg = expectedMsg + 'abcd';
-            sandbox.stub(element.$.confirmRevertSubmissionDialog,
+            sinon.stub(element.$.confirmRevertSubmissionDialog,
                 '_modifyRevertSubmissionMsg').returns(modifiedMsg);
             element.showRevertSubmissionDialog();
             flush(() => {
@@ -1881,7 +1877,7 @@
                 .returns(Promise.resolve({revert_changes: [
                   {change_id: 12345},
                 ]}));
-            navigateToSearchQueryStub = sandbox.stub(GerritNav,
+            navigateToSearchQueryStub = sinon.stub(GerritNav,
                 'navigateToSearchQuery');
           });
 
@@ -1906,8 +1902,8 @@
                   {change_id: 12345, topic: 'T'},
                   {change_id: 23456, topic: 'T'},
                 ]}));
-            showActionDialogStub = sandbox.stub(element, '_showActionDialog');
-            navigateToSearchQueryStub = sandbox.stub(GerritNav,
+            showActionDialogStub = sinon.stub(element, '_showActionDialog');
+            navigateToSearchQueryStub = sinon.stub(GerritNav,
                 'navigateToSearchQuery');
           });
 
@@ -1940,9 +1936,9 @@
 
       suite('failure modes', () => {
         test('non-latest', () => {
-          sandbox.stub(element, 'fetchChangeUpdates')
+          sinon.stub(element, 'fetchChangeUpdates')
               .returns(Promise.resolve({isLatest: false}));
-          const sendStub = sandbox.stub(element.$.restAPI,
+          const sendStub = sinon.stub(element.$.restAPI,
               'executeChangeAction');
 
           return element._send('DELETE', payload, '/endpoint', true, cleanup)
@@ -1955,15 +1951,15 @@
         });
 
         test('send fails', () => {
-          sandbox.stub(element, 'fetchChangeUpdates')
+          sinon.stub(element, 'fetchChangeUpdates')
               .returns(Promise.resolve({isLatest: true}));
-          const sendStub = sandbox.stub(element.$.restAPI,
-              'executeChangeAction',
+          const sendStub = sinon.stub(element.$.restAPI,
+              'executeChangeAction').callsFake(
               (num, method, patchNum, endpoint, payload, onErr) => {
                 onErr();
                 return Promise.resolve(null);
               });
-          const handleErrorStub = sandbox.stub(element, '_handleResponseError');
+          const handleErrorStub = sinon.stub(element, '_handleResponseError');
 
           return element._send('DELETE', payload, '/endpoint', true, cleanup)
               .then(() => {
@@ -1977,8 +1973,8 @@
     });
 
     test('_handleAction reports', () => {
-      sandbox.stub(element, '_fireAction');
-      const reportStub = sandbox.stub(element.reporting, 'reportInteraction');
+      sinon.stub(element, '_fireAction');
+      const reportStub = sinon.stub(element.reporting, 'reportInteraction');
       element._handleAction('type', 'key');
       assert.isTrue(reportStub.called);
       assert.equal(reportStub.lastCall.args[0], 'type-key');
@@ -1987,7 +1983,7 @@
 
   suite('getChangeRevisionActions returns only some actions', () => {
     let element;
-    let sandbox;
+
     let changeRevisionActions;
 
     setup(() => {
@@ -2001,8 +1997,7 @@
         getProjectConfig() { return Promise.resolve({}); },
       });
 
-      sandbox = sinon.sandbox.create();
-      sandbox.stub(pluginLoader, 'awaitPluginsLoaded')
+      sinon.stub(pluginLoader, 'awaitPluginsLoaded')
           .returns(Promise.resolve());
 
       element = basicFixture.instantiate();
@@ -2012,17 +2007,13 @@
       element.changeNum = '42';
       element.latestPatchNum = '2';
 
-      sandbox.stub(element.$.confirmCherrypick.$.restAPI,
+      sinon.stub(element.$.confirmCherrypick.$.restAPI,
           'getRepoBranches').returns(Promise.resolve([]));
-      sandbox.stub(element.$.confirmMove.$.restAPI,
+      sinon.stub(element.$.confirmMove.$.restAPI,
           'getRepoBranches').returns(Promise.resolve([]));
       return element.reload();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('confirmSubmitDialog and confirmRebase properties are changed', () => {
       changeRevisionActions = {};
       element.reload();
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.js
index 23c4d8a..50c2355 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.js
@@ -54,7 +54,6 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-change-metadata integration tests', () => {
-  let sandbox;
   let element;
 
   const sectionSelectors = [
@@ -89,7 +88,6 @@
   }
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
       getLoggedIn() { return Promise.resolve(false); },
@@ -98,7 +96,6 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     resetPlugins();
   });
 
@@ -122,7 +119,8 @@
         plugin.registerStyleModule('change-metadata', 'my-plugin-style');
       }, undefined, 'http://test.com/plugins/style.js');
       element = createElement();
-      sandbox.stub(pluginEndpoints, 'importUrl', url => Promise.resolve());
+      sinon.stub(pluginEndpoints, 'importUrl')
+          .callsFake( url => Promise.resolve());
       pluginLoader.loadPlugins([]);
       pluginLoader.awaitPluginsLoaded().then(() => {
         flush(done);
@@ -149,7 +147,7 @@
         plugin = p;
         plugin.registerStyleModule('change-metadata', 'my-plugin-style');
       }, undefined, 'http://test.com/plugins/style.js');
-      sandbox.stub(pluginLoader, 'arePluginsLoaded').returns(true);
+      sinon.stub(pluginLoader, 'arePluginsLoaded').returns(true);
       pluginLoader.loadPlugins([]);
       element = createElement();
     });
@@ -161,7 +159,7 @@
 
     test('labels changed callback', done => {
       let callCount = 0;
-      const labelChangeSpy = sandbox.spy(arg => {
+      const labelChangeSpy = sinon.spy(arg => {
         callCount++;
         if (callCount === 1) {
           assert.deepEqual(arg, labels);
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.js
index 27f9b85..26baf3c 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.js
@@ -29,21 +29,16 @@
 
 suite('gr-change-metadata tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
       getLoggedIn() { return Promise.resolve(false); },
     });
 
     element = basicFixture.instantiate();
-    sandbox.stub(pluginEndpoints, 'importUrl', url => Promise.resolve());
-  });
-
-  teardown(() => {
-    sandbox.restore();
+    sinon.stub(pluginEndpoints, 'importUrl')
+        .callsFake( url => Promise.resolve());
   });
 
   test('computed fields', () => {
@@ -111,7 +106,7 @@
   });
 
   test('weblinks use GerritNav interface', () => {
-    const weblinksStub = sandbox.stub(GerritNav, '_generateWeblinks')
+    const weblinksStub = sinon.stub(GerritNav, '_generateWeblinks')
         .returns([{name: 'stubb', url: '#s'}]);
     element.commitInfo = {};
     element.serverConfig = {};
@@ -154,7 +149,7 @@
 
   test('weblinks are visible when other weblinks', () => {
     const router = document.createElement('gr-router');
-    sandbox.stub(GerritNav, '_generateWeblinks',
+    sinon.stub(GerritNav, '_generateWeblinks').callsFake(
         router._generateWeblinks.bind(router));
 
     element.commitInfo = {web_links: [{name: 'test', url: '#'}]};
@@ -170,7 +165,7 @@
 
   test('weblinks are visible when gitiles and other weblinks', () => {
     const router = document.createElement('gr-router');
-    sandbox.stub(GerritNav, '_generateWeblinks',
+    sinon.stub(GerritNav, '_generateWeblinks').callsFake(
         router._generateWeblinks.bind(router));
 
     element.commitInfo = {
@@ -612,7 +607,7 @@
 
   suite('remove reviewer votes', () => {
     setup(() => {
-      sandbox.stub(element, '_computeTopicReadOnly').returns(true);
+      sinon.stub(element, '_computeTopicReadOnly').returns(true);
       element.change = {
         _number: 42,
         change_id: 'the id',
@@ -647,8 +642,8 @@
       let setStub;
 
       setup(() => {
-        deleteStub = sandbox.stub(element.$.restAPI, 'deleteAssignee');
-        setStub = sandbox.stub(element.$.restAPI, 'setAssignee');
+        deleteStub = sinon.stub(element.$.restAPI, 'deleteAssignee');
+        setStub = sinon.stub(element.$.restAPI, 'setAssignee');
         element.serverConfig = {
           change: {
             enable_assignee: true,
@@ -692,10 +687,10 @@
 
     test('changing topic', () => {
       const newTopic = 'the new topic';
-      sandbox.stub(element.$.restAPI, 'setChangeTopic').returns(
+      sinon.stub(element.$.restAPI, 'setChangeTopic').returns(
           Promise.resolve(newTopic));
       element._handleTopicChanged({}, newTopic);
-      const topicChangedSpy = sandbox.spy();
+      const topicChangedSpy = sinon.spy();
       element.addEventListener('topic-changed', topicChangedSpy);
       assert.isTrue(element.$.restAPI.setChangeTopic.calledWith(
           42, newTopic));
@@ -707,12 +702,12 @@
     });
 
     test('topic removal', () => {
-      sandbox.stub(element.$.restAPI, 'setChangeTopic').returns(
+      sinon.stub(element.$.restAPI, 'setChangeTopic').returns(
           Promise.resolve());
       const chip = element.shadowRoot
           .querySelector('gr-linked-chip');
       const remove = chip.$.remove;
-      const topicChangedSpy = sandbox.spy();
+      const topicChangedSpy = sinon.spy();
       element.addEventListener('topic-changed', topicChangedSpy);
       MockInteractions.tap(remove);
       assert.isTrue(chip.disabled);
@@ -730,7 +725,7 @@
       flushAsynchronousOperations();
       element._newHashtag = 'new hashtag';
       const newHashtag = ['new hashtag'];
-      sandbox.stub(element.$.restAPI, 'setChangeHashtag').returns(
+      sinon.stub(element.$.restAPI, 'setChangeHashtag').returns(
           Promise.resolve(newHashtag));
       element._handleHashtagChanged({}, 'new hashtag');
       assert.isTrue(element.$.restAPI.setChangeHashtag.calledWith(
@@ -750,7 +745,7 @@
     const label = element.shadowRoot
         .querySelector('.topicEditableLabel');
     assert.ok(label);
-    sandbox.stub(label, 'open');
+    sinon.stub(label, 'open');
     element.editTopic();
     flushAsynchronousOperations();
 
@@ -780,4 +775,4 @@
       });
     });
   });
-});
\ No newline at end of file
+});
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
index 3790e7f..d2e8ea7 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
@@ -36,7 +36,7 @@
 
 suite('gr-change-view tests', () => {
   let element;
-  let sandbox;
+
   let navigateToChangeStub;
 
   suiteSetup(() => {
@@ -285,10 +285,9 @@
   ];
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     // Since pluginEndpoints are global, must reset state.
     _testOnly_resetEndpoints();
-    navigateToChangeStub = sandbox.stub(GerritNav, 'navigateToChange');
+    navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({test: 'config'}); },
       getAccount() { return Promise.resolve(null); },
@@ -298,7 +297,7 @@
       _fetchSharedCacheURL() { return Promise.resolve({}); },
     });
     element = fixture.instantiate();
-    sandbox.stub(element.$.actions, 'reload').returns(Promise.resolve());
+    sinon.stub(element.$.actions, 'reload').returns(Promise.resolve());
     pluginLoader.loadPlugins([]);
     pluginApi.install(
         plugin => {
@@ -318,7 +317,6 @@
 
   teardown(done => {
     flush(() => {
-      sandbox.restore();
       done();
     });
   });
@@ -332,8 +330,8 @@
       basePatchNum: 'PARENT',
       patchNum: 1,
     };
-    const getUrlStub = sandbox.stub(GerritNav, 'getUrlForChange');
-    const replaceStateStub = sandbox.stub(history, 'replaceState');
+    const getUrlStub = sinon.stub(GerritNav, 'getUrlForChange');
+    const replaceStateStub = sinon.stub(history, 'replaceState');
     element._handleMessageAnchorTap({detail: {id: 'a12345'}});
 
     assert.equal(getUrlStub.lastCall.args[4], '#message-a12345');
@@ -346,8 +344,8 @@
       patchNum: 3,
       basePatchNum: 1,
     };
-    sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-    sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+    sinon.stub(element, 'computeLatestPatchNum').returns(10);
+    sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
     element._handleDiffAgainstBase(new CustomEvent(''));
     assert(navigateToChangeStub.called);
     const args = navigateToChangeStub.getCall(0).args;
@@ -361,8 +359,8 @@
       basePatchNum: 1,
       patchNum: 3,
     };
-    sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-    sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+    sinon.stub(element, 'computeLatestPatchNum').returns(10);
+    sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
     element._handleDiffAgainstLatest(new CustomEvent(''));
     assert(navigateToChangeStub.called);
     const args = navigateToChangeStub.getCall(0).args;
@@ -376,8 +374,8 @@
       patchNum: 3,
       basePatchNum: 1,
     };
-    sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-    sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+    sinon.stub(element, 'computeLatestPatchNum').returns(10);
+    sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
     element._handleDiffBaseAgainstLeft(new CustomEvent(''));
     assert(navigateToChangeStub.called);
     const args = navigateToChangeStub.getCall(0).args;
@@ -391,8 +389,8 @@
       basePatchNum: 1,
       patchNum: 3,
     };
-    sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-    sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+    sinon.stub(element, 'computeLatestPatchNum').returns(10);
+    sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
     element._handleDiffRightAgainstLatest(new CustomEvent(''));
     assert(navigateToChangeStub.called);
     const args = navigateToChangeStub.getCall(0).args;
@@ -406,8 +404,8 @@
       basePatchNum: 1,
       patchNum: 3,
     };
-    sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-    sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+    sinon.stub(element, 'computeLatestPatchNum').returns(10);
+    sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
     element._handleDiffBaseAgainstLatest(new CustomEvent(''));
     assert(navigateToChangeStub.called);
     const args = navigateToChangeStub.getCall(0).args;
@@ -501,19 +499,19 @@
 
   suite('keyboard shortcuts', () => {
     test('t to add topic', () => {
-      const editStub = sandbox.stub(element.$.metadata, 'editTopic');
+      const editStub = sinon.stub(element.$.metadata, 'editTopic');
       MockInteractions.pressAndReleaseKeyOn(element, 83, null, 't');
       assert(editStub.called);
     });
 
     test('S should toggle the CL star', () => {
-      const starStub = sandbox.stub(element.$.changeStar, 'toggleStar');
+      const starStub = sinon.stub(element.$.changeStar, 'toggleStar');
       MockInteractions.pressAndReleaseKeyOn(element, 83, null, 's');
       assert(starStub.called);
     });
 
     test('U should navigate to root if no backPage set', () => {
-      const relativeNavStub = sandbox.stub(GerritNav,
+      const relativeNavStub = sinon.stub(GerritNav,
           'navigateToRelativeUrl');
       MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u');
       assert.isTrue(relativeNavStub.called);
@@ -522,7 +520,7 @@
     });
 
     test('U should navigate to backPage if set', () => {
-      const relativeNavStub = sandbox.stub(GerritNav,
+      const relativeNavStub = sinon.stub(GerritNav,
           'navigateToRelativeUrl');
       element.backPage = '/dashboard/self';
       MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u');
@@ -532,8 +530,8 @@
     });
 
     test('A fires an error event when not logged in', done => {
-      sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(false));
-      const loggedInErrorSpy = sandbox.spy();
+      sinon.stub(element, '_getLoggedIn').returns(Promise.resolve(false));
+      const loggedInErrorSpy = sinon.spy();
       element.addEventListener('show-auth-required', loggedInErrorSpy);
       MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a');
       flush(() => {
@@ -544,7 +542,7 @@
     });
 
     test('shift A does not open reply overlay', done => {
-      sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
+      sinon.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
       MockInteractions.pressAndReleaseKeyOn(element, 65, 'shift', 'a');
       flush(() => {
         assert.isFalse(element.$.replyOverlay.opened);
@@ -553,11 +551,11 @@
     });
 
     test('A toggles overlay when logged in', done => {
-      sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
-      sandbox.stub(element.$.replyDialog, 'fetchChangeUpdates')
+      sinon.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
+      sinon.stub(element.$.replyDialog, 'fetchChangeUpdates')
           .returns(Promise.resolve({isLatest: true}));
       element._change = {labels: {}};
-      const openSpy = sandbox.spy(element, '_openReplyDialog');
+      const openSpy = sinon.spy(element, '_openReplyDialog');
 
       MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a');
       flush(() => {
@@ -587,7 +585,7 @@
           },
         },
       };
-      sandbox.spy(element, '_handleHideBackgroundContent');
+      sinon.spy(element, '_handleHideBackgroundContent');
       element.$.replyDialog.dispatchEvent(
           new CustomEvent('fullscreen-overlay-opened', {
             composed: true, bubbles: true,
@@ -612,7 +610,7 @@
           },
         },
       };
-      sandbox.spy(element, '_handleShowBackgroundContent');
+      sinon.spy(element, '_handleShowBackgroundContent');
       element.$.replyDialog.dispatchEvent(
           new CustomEvent('fullscreen-overlay-closed', {
             composed: true, bubbles: true,
@@ -623,7 +621,7 @@
 
     test('expand all messages when expand-diffs fired', () => {
       const handleExpand =
-          sandbox.stub(element.$.fileList, 'expandAllDiffs');
+          sinon.stub(element.$.fileList, 'expandAllDiffs');
       element.$.fileListHeader.dispatchEvent(
           new CustomEvent('expand-diffs', {
             composed: true, bubbles: true,
@@ -633,7 +631,7 @@
 
     test('collapse all messages when collapse-diffs fired', () => {
       const handleCollapse =
-      sandbox.stub(element.$.fileList, 'collapseAllDiffs');
+      sinon.stub(element.$.fileList, 'collapseAllDiffs');
       element.$.fileListHeader.dispatchEvent(
           new CustomEvent('collapse-diffs', {
             composed: true, bubbles: true,
@@ -643,7 +641,7 @@
 
     test('X should expand all messages', done => {
       flush(() => {
-        const handleExpand = sandbox.stub(element.messagesList,
+        const handleExpand = sinon.stub(element.messagesList,
             'handleExpandCollapse');
         MockInteractions.pressAndReleaseKeyOn(element, 88, null, 'x');
         assert(handleExpand.calledWith(true));
@@ -653,7 +651,7 @@
 
     test('Z should collapse all messages', done => {
       flush(() => {
-        const handleExpand = sandbox.stub(element.messagesList,
+        const handleExpand = sinon.stub(element.messagesList,
             'handleExpandCollapse');
         MockInteractions.pressAndReleaseKeyOn(element, 90, null, 'z');
         assert(handleExpand.calledWith(false));
@@ -681,8 +679,8 @@
           };
 
           navigateToChangeStub.restore();
-          navigateToChangeStub = sandbox.stub(GerritNav, 'navigateToChange',
-              (change, patchNum, basePatchNum) => {
+          navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange')
+              .callsFake((change, patchNum, basePatchNum) => {
                 assert.equal(change, element._change);
                 assert.isUndefined(patchNum);
                 assert.isUndefined(basePatchNum);
@@ -693,7 +691,7 @@
         });
 
     test('d should open download overlay', () => {
-      const stub = sandbox.stub(element.$.downloadOverlay, 'open').returns(
+      const stub = sinon.stub(element.$.downloadOverlay, 'open').returns(
           new Promise(resolve => {})
       );
       MockInteractions.pressAndReleaseKeyOn(element, 68, null, 'd');
@@ -701,7 +699,7 @@
     });
 
     test(', should open diff preferences', () => {
-      const stub = sandbox.stub(
+      const stub = sinon.stub(
           element.$.fileList.$.diffPreferencesDialog, 'open');
       element._loggedIn = false;
       element.disableDiffPrefs = true;
@@ -718,8 +716,8 @@
     });
 
     test('m should toggle diff mode', () => {
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
-      const setModeStub = sandbox.stub(element.$.fileListHeader,
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      const setModeStub = sinon.stub(element.$.fileListHeader,
           'setDiffViewMode');
       const e = {preventDefault: () => {}};
       flushAsynchronousOperations();
@@ -751,7 +749,7 @@
     setup(() => {
       // Fake computeDraftCount as its required for ChangeComments,
       // see gr-comment-api#reloadDrafts.
-      reloadStub = sandbox.stub(element.$.commentAPI, 'reloadDrafts')
+      reloadStub = sinon.stub(element.$.commentAPI, 'reloadDrafts')
           .returns(Promise.resolve({
             drafts,
             getAllThreadsForChange: () => ([]),
@@ -786,7 +784,7 @@
     setup(() => {
       // Fake computeDraftCount as its required for ChangeComments,
       // see gr-comment-api#reloadDrafts.
-      sandbox.stub(element.$.commentAPI, 'reloadDrafts')
+      sinon.stub(element.$.commentAPI, 'reloadDrafts')
           .returns(Promise.resolve({
             drafts: {},
             getAllThreadsForChange: () => THREADS,
@@ -833,7 +831,7 @@
   });
 
   test('diff comments modified', () => {
-    sandbox.spy(element, '_handleReloadCommentThreads');
+    sinon.spy(element, '_handleReloadCommentThreads');
     return element._reloadComments().then(() => {
       element.dispatchEvent(
           new CustomEvent('diff-comments-modified', {
@@ -844,7 +842,7 @@
   });
 
   test('thread list modified', () => {
-    sandbox.spy(element, '_handleReloadDiffComments');
+    sinon.spy(element, '_handleReloadDiffComments');
     element._activeTabs = [PrimaryTab.COMMENT_THREADS, SecondaryTab.CHANGE_LOG];
     flushAsynchronousOperations();
 
@@ -903,9 +901,9 @@
           },
         },
       };
-      sandbox.stub(element.$.relatedChanges, 'reload');
-      sandbox.stub(element, '_reload').returns(Promise.resolve());
-      sandbox.spy(element, '_paramsChanged');
+      sinon.stub(element.$.relatedChanges, 'reload');
+      sinon.stub(element, '_reload').returns(Promise.resolve());
+      sinon.spy(element, '_paramsChanged');
       element.params = {view: 'change', changeNum: '1'};
     });
   });
@@ -1003,7 +1001,7 @@
   });
 
   test('download tap calls _handleOpenDownloadDialog', () => {
-    sandbox.stub(element, '_handleOpenDownloadDialog');
+    sinon.stub(element, '_handleOpenDownloadDialog');
     element.$.actions.dispatchEvent(
         new CustomEvent('download-tap', {
           composed: true, bubbles: true,
@@ -1019,7 +1017,7 @@
   });
 
   test('_changeStatuses', () => {
-    sandbox.stub(element, 'changeStatuses').returns(
+    sinon.stub(element, 'changeStatuses').returns(
         ['Merged', 'WIP']);
     element._loading = false;
     element._change = {
@@ -1051,7 +1049,7 @@
   });
 
   test('diff preferences open when open-diff-prefs is fired', () => {
-    const overlayOpenStub = sandbox.stub(element.$.fileList,
+    const overlayOpenStub = sinon.stub(element.$.fileList,
         'openDiffPrefs');
     element.$.fileListHeader.dispatchEvent(
         new CustomEvent('open-diff-prefs', {
@@ -1109,7 +1107,7 @@
       },
     };
     flushAsynchronousOperations();
-    const reloadStub = sandbox.stub(element, '_reload');
+    const reloadStub = sinon.stub(element, '_reload');
     element.splice('_change.labels.test.all', 0, 1);
     assert.isFalse(reloadStub.called);
     element._change.labels.test.all.push(vote);
@@ -1214,7 +1212,7 @@
 
   test('_setDiffViewMode is called with reset when new change is loaded',
       () => {
-        sandbox.stub(element, '_setDiffViewMode');
+        sinon.stub(element, '_setDiffViewMode');
         element.viewState = {changeNum: 1};
         element._changeNum = 2;
         element._resetFileListViewState();
@@ -1229,7 +1227,7 @@
   });
 
   test('diffMode defaults to side by side without preferences', done => {
-    sandbox.stub(element.$.restAPI, 'getPreferences').returns(
+    sinon.stub(element.$.restAPI, 'getPreferences').returns(
         Promise.resolve({}));
     // No user prefs or diff view mode set.
 
@@ -1240,7 +1238,7 @@
   });
 
   test('diffMode defaults to preference when not already set', done => {
-    sandbox.stub(element.$.restAPI, 'getPreferences').returns(
+    sinon.stub(element.$.restAPI, 'getPreferences').returns(
         Promise.resolve({default_diff_view: 'UNIFIED'}));
 
     element._setDiffViewMode().then(() => {
@@ -1251,7 +1249,7 @@
 
   test('existing diffMode overrides preference', done => {
     element.viewState.diffMode = 'SIDE_BY_SIDE';
-    sandbox.stub(element.$.restAPI, 'getPreferences').returns(
+    sinon.stub(element.$.restAPI, 'getPreferences').returns(
         Promise.resolve({default_diff_view: 'UNIFIED'}));
     element._setDiffViewMode().then(() => {
       assert.equal(element.viewState.diffMode, 'SIDE_BY_SIDE');
@@ -1260,13 +1258,13 @@
   });
 
   test('don’t reload entire page when patchRange changes', () => {
-    const reloadStub = sandbox.stub(element, '_reload',
+    const reloadStub = sinon.stub(element, '_reload').callsFake(
         () => Promise.resolve());
-    const reloadPatchDependentStub = sandbox.stub(element,
-        '_reloadPatchNumDependentResources',
-        () => Promise.resolve());
-    const relatedClearSpy = sandbox.spy(element.$.relatedChanges, 'clear');
-    const collapseStub = sandbox.stub(element.$.fileList, 'collapseAllDiffs');
+    const reloadPatchDependentStub = sinon.stub(element,
+        '_reloadPatchNumDependentResources')
+        .callsFake(() => Promise.resolve());
+    const relatedClearSpy = sinon.spy(element.$.relatedChanges, 'clear');
+    const collapseStub = sinon.stub(element.$.fileList, 'collapseAllDiffs');
 
     const value = {
       view: GerritNav.View.CHANGE,
@@ -1288,9 +1286,9 @@
   });
 
   test('reload entire page when patchRange doesnt change', () => {
-    const reloadStub = sandbox.stub(element, '_reload',
+    const reloadStub = sinon.stub(element, '_reload').callsFake(
         () => Promise.resolve());
-    const collapseStub = sandbox.stub(element.$.fileList, 'collapseAllDiffs');
+    const collapseStub = sinon.stub(element.$.fileList, 'collapseAllDiffs');
     const value = {
       view: GerritNav.View.CHANGE,
     };
@@ -1303,8 +1301,8 @@
   });
 
   test('related changes are not updated after other action', done => {
-    sandbox.stub(element, '_reload', () => Promise.resolve());
-    sandbox.stub(element.$.relatedChanges, 'reload');
+    sinon.stub(element, '_reload').callsFake(() => Promise.resolve());
+    sinon.stub(element.$.relatedChanges, 'reload');
     const e = {detail: {action: 'abandon'}};
     element._handleReloadChange(e).then(() => {
       assert.isFalse(navigateToChangeStub.called);
@@ -1337,7 +1335,7 @@
       },
       current_revision: 'rev3',
     };
-    sandbox.stub(GerritNav, 'getUrlForChange')
+    sinon.stub(GerritNav, 'getUrlForChange')
         .returns('/change/123');
     assert.equal(
         element._computeCopyTextForTitle(change),
@@ -1379,7 +1377,7 @@
   });
 
   test('_handleCommitMessageSave trims trailing whitespace', () => {
-    const putStub = sandbox.stub(element.$.restAPI, 'putChangeCommitMessage')
+    const putStub = sinon.stub(element.$.restAPI, 'putChangeCommitMessage')
         .returns(Promise.resolve({}));
 
     const mockEvent = content => { return {detail: {content}}; };
@@ -1466,13 +1464,14 @@
   });
 
   test('topic is coalesced to null', done => {
-    sandbox.stub(element, '_changeChanged');
-    sandbox.stub(element.$.restAPI, 'getChangeDetail', () => Promise.resolve({
-      id: '123456789',
-      labels: {},
-      current_revision: 'foo',
-      revisions: {foo: {commit: {}}},
-    }));
+    sinon.stub(element, '_changeChanged');
+    sinon.stub(element.$.restAPI, 'getChangeDetail').callsFake(
+        () => Promise.resolve({
+          id: '123456789',
+          labels: {},
+          current_revision: 'foo',
+          revisions: {foo: {commit: {}}},
+        }));
 
     element._getChangeDetail().then(() => {
       assert.isNull(element._change.topic);
@@ -1481,13 +1480,14 @@
   });
 
   test('commit sha is populated from getChangeDetail', done => {
-    sandbox.stub(element, '_changeChanged');
-    sandbox.stub(element.$.restAPI, 'getChangeDetail', () => Promise.resolve({
-      id: '123456789',
-      labels: {},
-      current_revision: 'foo',
-      revisions: {foo: {commit: {}}},
-    }));
+    sinon.stub(element, '_changeChanged');
+    sinon.stub(element.$.restAPI, 'getChangeDetail').callsFake(
+        () => Promise.resolve({
+          id: '123456789',
+          labels: {},
+          current_revision: 'foo',
+          revisions: {foo: {commit: {}}},
+        }));
 
     element._getChangeDetail().then(() => {
       assert.equal('foo', element._commitInfo.commit);
@@ -1496,14 +1496,15 @@
   });
 
   test('edit is added to change', () => {
-    sandbox.stub(element, '_changeChanged');
-    sandbox.stub(element.$.restAPI, 'getChangeDetail', () => Promise.resolve({
-      id: '123456789',
-      labels: {},
-      current_revision: 'foo',
-      revisions: {foo: {commit: {}}},
-    }));
-    sandbox.stub(element, '_getEdit', () => Promise.resolve({
+    sinon.stub(element, '_changeChanged');
+    sinon.stub(element.$.restAPI, 'getChangeDetail').callsFake(
+        () => Promise.resolve({
+          id: '123456789',
+          labels: {},
+          current_revision: 'foo',
+          revisions: {foo: {commit: {}}},
+        }));
+    sinon.stub(element, '_getEdit').callsFake(() => Promise.resolve({
       base_patch_set_number: 1,
       commit: {commit: 'bar'},
     }));
@@ -1571,7 +1572,7 @@
 
   test('_openReplyDialog called with `ANY` when coming from tap event',
       () => {
-        const openStub = sandbox.stub(element, '_openReplyDialog');
+        const openStub = sinon.stub(element, '_openReplyDialog');
         element._serverConfig = {};
         MockInteractions.tap(element.$.replyBtn);
         assert(openStub.lastCall.calledWithExactly(
@@ -1583,7 +1584,7 @@
   test('_openReplyDialog called with `BODY` when coming from message reply' +
       'event', done => {
     flush(() => {
-      const openStub = sandbox.stub(element, '_openReplyDialog');
+      const openStub = sinon.stub(element, '_openReplyDialog');
       element.messagesList.dispatchEvent(
           new CustomEvent('reply', {
             detail:
@@ -1600,7 +1601,7 @@
 
   test('reply dialog focus can be controlled', () => {
     const FocusTarget = element.$.replyDialog.FocusTarget;
-    const openStub = sandbox.stub(element, '_openReplyDialog');
+    const openStub = sinon.stub(element, '_openReplyDialog');
 
     const e = {detail: {}};
     element._handleShowReplyDialog(e);
@@ -1616,7 +1617,7 @@
   });
 
   test('getUrlParameter functionality', () => {
-    const locationStub = sandbox.stub(element, '_getLocationSearch');
+    const locationStub = sinon.stub(element, '_getLocationSearch');
 
     locationStub.returns('?test');
     assert.equal(element._getUrlParameter('test'), 'test');
@@ -1631,8 +1632,10 @@
   });
 
   test('revert dialog opened with revert param', done => {
-    sandbox.stub(element.$.restAPI, 'getLoggedIn', () => Promise.resolve(true));
-    sandbox.stub(pluginLoader, 'awaitPluginsLoaded', () => Promise.resolve());
+    sinon.stub(element.$.restAPI, 'getLoggedIn')
+        .callsFake(() => Promise.resolve(true));
+    sinon.stub(pluginLoader, 'awaitPluginsLoaded')
+        .callsFake(() => Promise.resolve());
 
     element._patchRange = {
       basePatchNum: 'PARENT',
@@ -1650,13 +1653,13 @@
       actions: {},
     };
 
-    sandbox.stub(element, '_getUrlParameter',
+    sinon.stub(element, '_getUrlParameter').callsFake(
         param => {
           assert.equal(param, 'revert');
           return param;
         });
 
-    sandbox.stub(element.$.actions, 'showRevertDialog',
+    sinon.stub(element.$.actions, 'showRevertDialog').callsFake(
         done);
 
     element._maybeShowRevertDialog();
@@ -1666,7 +1669,7 @@
   suite('scroll related tests', () => {
     test('document scrolling calls function to set scroll height', done => {
       const originalHeight = document.body.scrollHeight;
-      const scrollStub = sandbox.stub(element, '_handleScroll',
+      const scrollStub = sinon.stub(element, '_handleScroll').callsFake(
           () => {
             assert.isTrue(scrollStub.called);
             document.body.style.height = originalHeight + 'px';
@@ -1680,7 +1683,7 @@
     test('scrollTop is set correctly', () => {
       element.viewState = {scrollTop: TEST_SCROLL_TOP_PX};
 
-      sandbox.stub(element, '_reload', () => {
+      sinon.stub(element, '_reload').callsFake(() => {
         // When element is reloaded, ensure that the history
         // state has the scrollTop set earlier. This will then
         // be reset.
@@ -1701,8 +1704,8 @@
 
   suite('reply dialog tests', () => {
     setup(() => {
-      sandbox.stub(element.$.replyDialog, '_draftChanged');
-      sandbox.stub(element.$.replyDialog, 'fetchChangeUpdates',
+      sinon.stub(element.$.replyDialog, '_draftChanged');
+      sinon.stub(element.$.replyDialog, 'fetchChangeUpdates').callsFake(
           () => Promise.resolve({isLatest: true}));
       element._change = {labels: {}};
     });
@@ -1735,7 +1738,7 @@
       const div = document.createElement('div');
       element.$.replyDialog.draft = '> quote text\n\n some draft text';
       element.$.replyDialog.quote = '> quote text\n\n';
-      const e = {target: div, preventDefault: sandbox.spy()};
+      const e = {target: div, preventDefault: sinon.spy()};
       element._handleReplyTap(e);
       assert.equal(element.$.replyDialog.draft,
           '> quote text\n\n some draft text');
@@ -1751,7 +1754,7 @@
 
   suite('commit message expand/collapse', () => {
     setup(() => {
-      sandbox.stub(element, 'fetchChangeUpdates',
+      sinon.stub(element, 'fetchChangeUpdates').callsFake(
           () => Promise.resolve({isLatest: false}));
     });
 
@@ -1782,17 +1785,18 @@
   suite('related changes expand/collapse', () => {
     let updateHeightSpy;
     setup(() => {
-      updateHeightSpy = sandbox.spy(element, '_updateRelatedChangeMaxHeight');
+      updateHeightSpy = sinon.spy(element, '_updateRelatedChangeMaxHeight');
     });
 
     test('relatedChangesToggle shown height greater than changeInfo height',
         () => {
           assert.isFalse(element.$.relatedChangesToggle.classList
               .contains('showToggle'));
-          sandbox.stub(element, '_getOffsetHeight', () => 50);
-          sandbox.stub(element, '_getScrollHeight', () => 60);
-          sandbox.stub(element, '_getLineHeight', () => 5);
-          sandbox.stub(window, 'matchMedia', () => { return {matches: true}; });
+          sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+          sinon.stub(element, '_getScrollHeight').callsFake(() => 60);
+          sinon.stub(element, '_getLineHeight').callsFake(() => 5);
+          sinon.stub(window, 'matchMedia')
+              .callsFake(() => { return {matches: true}; });
           element.$.relatedChanges.dispatchEvent(
               new CustomEvent('new-section-loaded'));
           assert.isTrue(element.$.relatedChangesToggle.classList
@@ -1804,10 +1808,11 @@
         () => {
           assert.isFalse(element.$.relatedChangesToggle.classList
               .contains('showToggle'));
-          sandbox.stub(element, '_getOffsetHeight', () => 50);
-          sandbox.stub(element, '_getScrollHeight', () => 40);
-          sandbox.stub(element, '_getLineHeight', () => 5);
-          sandbox.stub(window, 'matchMedia', () => { return {matches: true}; });
+          sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+          sinon.stub(element, '_getScrollHeight').callsFake(() => 40);
+          sinon.stub(element, '_getLineHeight').callsFake(() => 5);
+          sinon.stub(window, 'matchMedia')
+              .callsFake(() => { return {matches: true}; });
           element.$.relatedChanges.dispatchEvent(
               new CustomEvent('new-section-loaded'));
           assert.isFalse(element.$.relatedChangesToggle.classList
@@ -1816,8 +1821,9 @@
         });
 
     test('relatedChangesToggle functions', () => {
-      sandbox.stub(element, '_getOffsetHeight', () => 50);
-      sandbox.stub(window, 'matchMedia', () => { return {matches: false}; });
+      sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+      sinon.stub(window, 'matchMedia')
+          .callsFake(() => { return {matches: false}; });
       element._relatedChangesLoading = false;
       assert.isTrue(element._relatedChangesCollapsed);
       assert.isTrue(
@@ -1829,9 +1835,10 @@
     });
 
     test('_updateRelatedChangeMaxHeight without commit toggle', () => {
-      sandbox.stub(element, '_getOffsetHeight', () => 50);
-      sandbox.stub(element, '_getLineHeight', () => 12);
-      sandbox.stub(window, 'matchMedia', () => { return {matches: false}; });
+      sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+      sinon.stub(element, '_getLineHeight').callsFake(() => 12);
+      sinon.stub(window, 'matchMedia')
+          .callsFake(() => { return {matches: false}; });
 
       // 50 (existing height) - 30 (extra height) = 20 (adjusted height).
       // 20 (max existing height)  % 12 (line height) = 6 (remainder).
@@ -1846,9 +1853,10 @@
 
     test('_updateRelatedChangeMaxHeight with commit toggle', () => {
       element._latestCommitMessage = _.times(31, String).join('\n');
-      sandbox.stub(element, '_getOffsetHeight', () => 50);
-      sandbox.stub(element, '_getLineHeight', () => 12);
-      sandbox.stub(window, 'matchMedia', () => { return {matches: false}; });
+      sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+      sinon.stub(element, '_getLineHeight').callsFake(() => 12);
+      sinon.stub(window, 'matchMedia')
+          .callsFake(() => { return {matches: false}; });
 
       // 50 (existing height) % 12 (line height) = 2 (remainder).
       // 50 (existing height)  - 2 (remainder) = 48 (max height to set).
@@ -1862,9 +1870,10 @@
 
     test('_updateRelatedChangeMaxHeight in small screen mode', () => {
       element._latestCommitMessage = _.times(31, String).join('\n');
-      sandbox.stub(element, '_getOffsetHeight', () => 50);
-      sandbox.stub(element, '_getLineHeight', () => 12);
-      sandbox.stub(window, 'matchMedia', () => { return {matches: true}; });
+      sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+      sinon.stub(element, '_getLineHeight').callsFake(() => 12);
+      sinon.stub(window, 'matchMedia')
+          .callsFake(() => { return {matches: true}; });
 
       element._updateRelatedChangeMaxHeight();
 
@@ -1877,9 +1886,9 @@
 
     test('_updateRelatedChangeMaxHeight in medium screen mode', () => {
       element._latestCommitMessage = _.times(31, String).join('\n');
-      sandbox.stub(element, '_getOffsetHeight', () => 50);
-      sandbox.stub(element, '_getLineHeight', () => 12);
-      sandbox.stub(window, 'matchMedia', () => {
+      sinon.stub(element, '_getOffsetHeight').callsFake(() => 50);
+      sinon.stub(element, '_getLineHeight').callsFake(() => 12);
+      sinon.stub(window, 'matchMedia').callsFake(() => {
         if (window.matchMedia.lastCall.args[0] === '(max-width: 75em)') {
           return {matches: true};
         } else {
@@ -1896,8 +1905,8 @@
 
     suite('update checks', () => {
       setup(() => {
-        sandbox.spy(element, '_startUpdateCheckTimer');
-        sandbox.stub(element, 'async', f => {
+        sinon.spy(element, '_startUpdateCheckTimer');
+        sinon.stub(element, 'async').callsFake( f => {
           // Only fire the async callback one time.
           if (element.async.callCount > 1) { return; }
           f.call(element);
@@ -1905,7 +1914,7 @@
       });
 
       test('_startUpdateCheckTimer negative delay', () => {
-        sandbox.stub(element, 'fetchChangeUpdates');
+        sinon.stub(element, 'fetchChangeUpdates');
 
         element._serverConfig = {change: {update_delay: -1}};
 
@@ -1914,7 +1923,7 @@
       });
 
       test('_startUpdateCheckTimer up-to-date', () => {
-        sandbox.stub(element, 'fetchChangeUpdates',
+        sinon.stub(element, 'fetchChangeUpdates').callsFake(
             () => Promise.resolve({isLatest: true}));
 
         element._serverConfig = {change: {update_delay: 12345}};
@@ -1925,7 +1934,7 @@
       });
 
       test('_startUpdateCheckTimer out-of-date shows an alert', done => {
-        sandbox.stub(element, 'fetchChangeUpdates',
+        sinon.stub(element, 'fetchChangeUpdates').callsFake(
             () => Promise.resolve({isLatest: false}));
         element.addEventListener('show-alert', e => {
           assert.equal(e.detail.message,
@@ -1936,7 +1945,7 @@
       });
 
       test('_startUpdateCheckTimer new status shows an alert', done => {
-        sandbox.stub(element, 'fetchChangeUpdates')
+        sinon.stub(element, 'fetchChangeUpdates')
             .returns(Promise.resolve({
               isLatest: true,
               newStatus: ChangeStatus.MERGED,
@@ -1949,7 +1958,7 @@
       });
 
       test('_startUpdateCheckTimer new messages shows an alert', done => {
-        sandbox.stub(element, 'fetchChangeUpdates')
+        sinon.stub(element, 'fetchChangeUpdates')
             .returns(Promise.resolve({
               isLatest: true,
               newMessages: true,
@@ -1992,7 +2001,7 @@
 
   test('_maybeScrollToMessage', done => {
     flush(() => {
-      const scrollStub = sandbox.stub(element.messagesList,
+      const scrollStub = sinon.stub(element.messagesList,
           'scrollToMessage');
 
       element._maybeScrollToMessage('');
@@ -2007,7 +2016,7 @@
   });
 
   test('topic update reloads related changes', () => {
-    sandbox.stub(element.$.relatedChanges, 'reload');
+    sinon.stub(element.$.relatedChanges, 'reload');
     element.dispatchEvent(new CustomEvent('topic-changed'));
     assert.isTrue(element.$.relatedChanges.reload.calledOnce);
   });
@@ -2074,11 +2083,11 @@
     flushAsynchronousOperations();
     const controls = element.$.fileListHeader
         .shadowRoot.querySelector('#editControls');
-    sandbox.stub(controls, 'openDeleteDialog');
-    sandbox.stub(controls, 'openRenameDialog');
-    sandbox.stub(controls, 'openRestoreDialog');
-    sandbox.stub(GerritNav, 'getEditUrlForDiff');
-    sandbox.stub(GerritNav, 'navigateToRelativeUrl');
+    sinon.stub(controls, 'openDeleteDialog');
+    sinon.stub(controls, 'openRenameDialog');
+    sinon.stub(controls, 'openRestoreDialog');
+    sinon.stub(GerritNav, 'getEditUrlForDiff');
+    sinon.stub(GerritNav, 'navigateToRelativeUrl');
 
     // Delete
     fileList.dispatchEvent(new CustomEvent('file-action-tap', {
@@ -2130,7 +2139,7 @@
   test('_selectedRevision updates when patchNum is changed', () => {
     const revision1 = {_number: 1, commit: {parents: []}};
     const revision2 = {_number: 2, commit: {parents: []}};
-    sandbox.stub(element.$.restAPI, 'getChangeDetail').returns(
+    sinon.stub(element.$.restAPI, 'getChangeDetail').returns(
         Promise.resolve({
           revisions: {
             aaa: revision1,
@@ -2141,8 +2150,8 @@
           current_revision: 'bbb',
           change_id: 'loremipsumdolorsitamet',
         }));
-    sandbox.stub(element, '_getEdit').returns(Promise.resolve());
-    sandbox.stub(element, '_getPreferences').returns(Promise.resolve({}));
+    sinon.stub(element, '_getEdit').returns(Promise.resolve());
+    sinon.stub(element, '_getPreferences').returns(Promise.resolve({}));
     element._patchRange = {patchNum: '2'};
     return element._getChangeDetail().then(() => {
       assert.strictEqual(element._selectedRevision, revision2);
@@ -2156,7 +2165,7 @@
     const revision1 = {_number: 1, commit: {parents: []}};
     const revision2 = {_number: 2, commit: {parents: []}};
     const revision3 = {_number: 'edit', commit: {parents: []}};
-    sandbox.stub(element.$.restAPI, 'getChangeDetail').returns(
+    sinon.stub(element.$.restAPI, 'getChangeDetail').returns(
         Promise.resolve({
           revisions: {
             aaa: revision1,
@@ -2168,8 +2177,8 @@
           current_revision: 'ccc',
           change_id: 'loremipsumdolorsitamet',
         }));
-    sandbox.stub(element, '_getEdit').returns(Promise.resolve());
-    sandbox.stub(element, '_getPreferences').returns(Promise.resolve({}));
+    sinon.stub(element, '_getEdit').returns(Promise.resolve());
+    sinon.stub(element, '_getPreferences').returns(Promise.resolve({}));
     element._patchRange = {patchNum: 'edit'};
     return element._getChangeDetail().then(() => {
       assert.strictEqual(element._selectedRevision, revision3);
@@ -2180,7 +2189,7 @@
     element._change = {labels: {}};
     element._patchRange = {patchNum: 4};
     element._mergeable = true;
-    const showStub = sandbox.stub(element.$.jsAPI, 'handleEvent');
+    const showStub = sinon.stub(element.$.jsAPI, 'handleEvent');
     element._sendShowChangeEvent();
     assert.isTrue(showStub.calledOnce);
     assert.equal(
@@ -2205,7 +2214,7 @@
     });
 
     test('edit exists in revisions', done => {
-      sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
+      sinon.stub(GerritNav, 'navigateToChange').callsFake((...args) => {
         assert.equal(args.length, 2);
         assert.equal(args[1], element.EDIT_NAME); // patchNum
         done();
@@ -2218,7 +2227,7 @@
     });
 
     test('no edit exists in revisions, non-latest patchset', done => {
-      sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
+      sinon.stub(GerritNav, 'navigateToChange').callsFake((...args) => {
         assert.equal(args.length, 4);
         assert.equal(args[1], 1); // patchNum
         assert.equal(args[3], true); // opt_isEdit
@@ -2233,7 +2242,7 @@
     });
 
     test('no edit exists in revisions, latest patchset', done => {
-      sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
+      sinon.stub(GerritNav, 'navigateToChange').callsFake((...args) => {
         assert.equal(args.length, 4);
         // No patch should be specified when patchNum == latest.
         assert.isNotOk(args[1]); // patchNum
@@ -2250,10 +2259,10 @@
   });
 
   test('_handleStopEditTap', done => {
-    sandbox.stub(element.$.metadata, '_computeLabelNames');
+    sinon.stub(element.$.metadata, '_computeLabelNames');
     navigateToChangeStub.restore();
-    sandbox.stub(element, 'computeLatestPatchNum').returns(1);
-    sandbox.stub(GerritNav, 'navigateToChange', (...args) => {
+    sinon.stub(element, 'computeLatestPatchNum').returns(1);
+    sinon.stub(GerritNav, 'navigateToChange').callsFake((...args) => {
       assert.equal(args.length, 2);
       assert.equal(args[1], 1); // patchNum
       done();
@@ -2293,7 +2302,7 @@
 
     setup(() => {
       element._change = {labels: {}};
-      getMergeableStub = sandbox.stub(element.$.restAPI, 'getMergeable')
+      getMergeableStub = sinon.stub(element.$.restAPI, 'getMergeable')
           .returns(Promise.resolve({mergeable: true}));
     });
 
@@ -2325,9 +2334,9 @@
   });
 
   test('_paramsChanged sets in projectLookup', () => {
-    sandbox.stub(element.$.relatedChanges, 'reload');
-    sandbox.stub(element, '_reload').returns(Promise.resolve());
-    const setStub = sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+    sinon.stub(element.$.relatedChanges, 'reload');
+    sinon.stub(element, '_reload').returns(Promise.resolve());
+    const setStub = sinon.stub(element.$.restAPI, 'setInProjectLookup');
     element._paramsChanged({
       view: GerritNav.View.CHANGE,
       changeNum: 101,
@@ -2343,7 +2352,7 @@
       starred: false,
     };
     element._loggedIn = true;
-    const stub = sandbox.stub(element, '_handleToggleStar');
+    const stub = sinon.stub(element, '_handleToggleStar');
     flushAsynchronousOperations();
 
     MockInteractions.tap(element.$.changeStar.shadowRoot
@@ -2357,19 +2366,19 @@
         basePatchNum: 'PARENT',
         patchNum: 1,
       };
-      sandbox.stub(element, '_getChangeDetail').returns(Promise.resolve());
-      sandbox.stub(element, '_getProjectConfig').returns(Promise.resolve());
-      sandbox.stub(element, '_reloadComments').returns(Promise.resolve());
-      sandbox.stub(element, '_getMergeability').returns(Promise.resolve());
-      sandbox.stub(element, '_getLatestCommitMessage')
+      sinon.stub(element, '_getChangeDetail').returns(Promise.resolve());
+      sinon.stub(element, '_getProjectConfig').returns(Promise.resolve());
+      sinon.stub(element, '_reloadComments').returns(Promise.resolve());
+      sinon.stub(element, '_getMergeability').returns(Promise.resolve());
+      sinon.stub(element, '_getLatestCommitMessage')
           .returns(Promise.resolve());
     });
 
     test('don\'t report changedDisplayed on reply', done => {
       const changeDisplayStub =
-        sandbox.stub(element.reporting, 'changeDisplayed');
+        sinon.stub(element.reporting, 'changeDisplayed');
       const changeFullyLoadedStub =
-        sandbox.stub(element.reporting, 'changeFullyLoaded');
+        sinon.stub(element.reporting, 'changeFullyLoaded');
       element._handleReplySent();
       flush(() => {
         assert.isFalse(changeDisplayStub.called);
@@ -2380,9 +2389,9 @@
 
     test('report changedDisplayed on _paramsChanged', done => {
       const changeDisplayStub =
-        sandbox.stub(element.reporting, 'changeDisplayed');
+        sinon.stub(element.reporting, 'changeDisplayed');
       const changeFullyLoadedStub =
-        sandbox.stub(element.reporting, 'changeFullyLoaded');
+        sinon.stub(element.reporting, 'changeFullyLoaded');
       element._paramsChanged({
         view: GerritNav.View.CHANGE,
         changeNum: 101,
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.js
index 1de2964..704d83f 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.js
@@ -24,15 +24,12 @@
 
 suite('gr-comment-list tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(GerritNav, 'mapCommentlinks', x => x);
-  });
 
-  teardown(() => { sandbox.restore(); });
+    sinon.stub(GerritNav, 'mapCommentlinks').callsFake( x => x);
+  });
 
   test('_computeFilesFromComments w/ special file path sorting', () => {
     const comments = {
@@ -91,7 +88,7 @@
   });
 
   test('_computeDiffLineURL', () => {
-    const getUrlStub = sandbox.stub(GerritNav, 'getUrlForDiffById');
+    const getUrlStub = sinon.stub(GerritNav, 'getUrlForDiffById');
     element.projectName = 'proj';
     element.changeNum = 123;
 
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.js b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.js
index 8af988e..c120c33 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.js
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.js
@@ -24,19 +24,13 @@
 
 suite('gr-commit-info tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('weblinks use GerritNav interface', () => {
-    const weblinksStub = sandbox.stub(GerritNav, '_generateWeblinks')
+    const weblinksStub = sinon.stub(GerritNav, '_generateWeblinks')
         .returns([{name: 'stubb', url: '#s'}]);
     element.change = {};
     element.commitInfo = {};
@@ -55,7 +49,7 @@
 
   test('use web link when available', () => {
     const router = document.createElement('gr-router');
-    sandbox.stub(GerritNav, '_generateWeblinks',
+    sinon.stub(GerritNav, '_generateWeblinks').callsFake(
         router._generateWeblinks.bind(router));
 
     element.change = {labels: [], project: ''};
@@ -71,7 +65,7 @@
 
   test('does not relativize web links that begin with scheme', () => {
     const router = document.createElement('gr-router');
-    sandbox.stub(GerritNav, '_generateWeblinks',
+    sinon.stub(GerritNav, '_generateWeblinks').callsFake(
         router._generateWeblinks.bind(router));
 
     element.change = {labels: [], project: ''};
@@ -89,7 +83,7 @@
 
   test('ignore web links that are neither gitweb nor gitiles', () => {
     const router = document.createElement('gr-router');
-    sandbox.stub(GerritNav, '_generateWeblinks',
+    sinon.stub(GerritNav, '_generateWeblinks').callsFake(
         router._generateWeblinks.bind(router));
 
     element.change = {project: 'project-name'};
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.js
index 34b59e1..14d16f5 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.js
@@ -22,22 +22,16 @@
 
 suite('gr-confirm-abandon-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_handleConfirmTap', () => {
-    const confirmHandler = sandbox.stub();
+    const confirmHandler = sinon.stub();
     element.addEventListener('confirm', confirmHandler);
-    sandbox.spy(element, '_handleConfirmTap');
-    sandbox.spy(element, '_confirm');
+    sinon.spy(element, '_handleConfirmTap');
+    sinon.spy(element, '_confirm');
     element.shadowRoot
         .querySelector('gr-dialog').dispatchEvent(
             new CustomEvent('confirm', {
@@ -51,9 +45,9 @@
   });
 
   test('_handleCancelTap', () => {
-    const cancelHandler = sandbox.stub();
+    const cancelHandler = sinon.stub();
     element.addEventListener('cancel', cancelHandler);
-    sandbox.spy(element, '_handleCancelTap');
+    sinon.spy(element, '_handleCancelTap');
     element.shadowRoot
         .querySelector('gr-dialog').dispatchEvent(
             new CustomEvent('cancel', {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.js
index a8528c5..c98353b 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.js
@@ -23,19 +23,15 @@
 
 suite('gr-confirm-cherrypick-conflict-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('_handleConfirmTap', () => {
-    const confirmHandler = sandbox.stub();
+    const confirmHandler = sinon.stub();
     element.addEventListener('confirm', confirmHandler);
-    sandbox.spy(element, '_handleConfirmTap');
+    sinon.spy(element, '_handleConfirmTap');
     element.shadowRoot
         .querySelector('gr-dialog').dispatchEvent(
             new CustomEvent('confirm', {
@@ -48,9 +44,9 @@
   });
 
   test('_handleCancelTap', () => {
-    const cancelHandler = sandbox.stub();
+    const cancelHandler = sinon.stub();
     element.addEventListener('cancel', cancelHandler);
-    sandbox.spy(element, '_handleCancelTap');
+    sinon.spy(element, '_handleCancelTap');
     element.shadowRoot
         .querySelector('gr-dialog').dispatchEvent(
             new CustomEvent('cancel', {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js
index 1f4e214..900412a 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js
@@ -26,10 +26,8 @@
 };
 suite('gr-confirm-cherrypick-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getRepoBranches(input) {
         if (input.startsWith('test')) {
@@ -49,8 +47,6 @@
     element.project = 'test-project';
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('with merged change', () => {
     element.changeStatus = 'MERGED';
     element.commitMessage = 'message\n';
@@ -117,7 +113,7 @@
 
     test('cherry pick topic submit', done => {
       element.branch = 'master';
-      const executeChangeActionStub = sandbox.stub(element.$.restAPI,
+      const executeChangeActionStub = sinon.stub(element.$.restAPI,
           'executeChangeAction').returns(Promise.resolve([]));
       MockInteractions.tap(element.shadowRoot.
           querySelector('gr-dialog').$.confirm);
@@ -141,7 +137,6 @@
     });
 
     test('submit button is blocked while cherry picks is running', done => {
-      console.log(element);
       const confirmButton = element.shadowRoot.querySelector('gr-dialog').$
           .confirm;
       assert.isFalse(confirmButton.hasAttribute('disabled'));
@@ -154,7 +149,7 @@
   });
 
   test('resetFocus', () => {
-    const focusStub = sandbox.stub(element.$.branchInput, 'focus');
+    const focusStub = sinon.stub(element.$.branchInput, 'focus');
     element.resetFocus();
     assert.isTrue(focusStub.called);
   });
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.js
index d50e889..498d31c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.js
@@ -22,15 +22,9 @@
 
 suite('gr-confirm-rebase-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('controls with parent and rebase on current available', () => {
@@ -124,7 +118,7 @@
         },
       ];
 
-      sandbox.stub(element.$.restAPI, 'getChanges').returns(Promise.resolve(
+      sinon.stub(element.$.restAPI, 'getChanges').returns(Promise.resolve(
           [
             {
               _number: 123,
@@ -143,7 +137,7 @@
     });
 
     test('_getRecentChanges', () => {
-      sandbox.spy(element, '_getRecentChanges');
+      sinon.spy(element, '_getRecentChanges');
       return element._getRecentChanges()
           .then(() => {
             assert.deepEqual(element._recentChanges, recentChanges);
@@ -173,7 +167,7 @@
     });
 
     test('input text change triggers function', () => {
-      sandbox.spy(element, '_getRecentChanges');
+      sinon.spy(element, '_getRecentChanges');
       element.$.parentInput.noDebounce = true;
       MockInteractions.pressAndReleaseKeyOn(
           element.$.parentInput.$.input,
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.js
index 5d1a4eb..7c84043 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.js
@@ -22,18 +22,14 @@
 
 suite('gr-confirm-revert-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox =sinon.sandbox.create();
   });
 
-  teardown(() => sandbox.restore());
-
   test('no match', () => {
     assert.isNotOk(element._message);
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     element.addEventListener('show-alert', alertStub);
     element._populateRevertSingleChangeMessage({},
         'not a commitHash in sight', undefined);
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.js
index 2b5a064..e2f2e9e 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.js
@@ -22,18 +22,14 @@
 
 suite('gr-confirm-revert-submission-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
   });
 
-  teardown(() => sandbox.restore());
-
   test('no match', () => {
     assert.isNotOk(element.message);
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     element.addEventListener('show-alert', alertStub);
     element._populateRevertSubmissionMessage(
         'not a commitHash in sight', {}
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.js
index ff5478f..77331f7 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.js
@@ -22,17 +22,11 @@
 
 suite('gr-file-list-header tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('display', () => {
     element.action = {label: 'my-label'};
     element.change = {
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.js
index 5b7cbd5..5dd5de7 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.js
@@ -101,11 +101,8 @@
 
 suite('gr-download-dialog', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     element = basicFixture.instantiate();
     element.patchNum = '1';
     element.config = {
@@ -121,10 +118,6 @@
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('anchors use download attribute', () => {
     const anchors = Array.from(
         dom(element.root).querySelectorAll('a'));
@@ -138,7 +131,7 @@
     });
 
     test('focuses on first download link if no copy links', () => {
-      const focusStub = sandbox.stub(element.$.download, 'focus');
+      const focusStub = sinon.stub(element.$.download, 'focus');
       element.focus();
       assert.isTrue(focusStub.called);
       focusStub.restore();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js
index 30a012e..d756bf3 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js
@@ -25,10 +25,8 @@
 
 suite('gr-file-list-header tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({test: 'config'}); },
       getAccount() { return Promise.resolve(null); },
@@ -39,7 +37,6 @@
 
   teardown(done => {
     flush(() => {
-      sandbox.restore();
       done();
     });
   });
@@ -80,7 +77,7 @@
   });
 
   test('description editing', () => {
-    const putDescStub = sandbox.stub(element.$.restAPI, 'setDescription')
+    const putDescStub = sinon.stub(element.$.restAPI, 'setDescription')
         .returns(Promise.resolve({ok: true}));
 
     element.changeNum = '42';
@@ -149,7 +146,7 @@
   test('expandAllDiffs called when expand button clicked', () => {
     element.shownFileCount = 1;
     flushAsynchronousOperations();
-    sandbox.stub(element, '_expandAllDiffs');
+    sinon.stub(element, '_expandAllDiffs');
     MockInteractions.tap(dom(element.root).querySelector(
         '#expandBtn'));
     assert.isTrue(element._expandAllDiffs.called);
@@ -158,14 +155,14 @@
   test('collapseAllDiffs called when expand button clicked', () => {
     element.shownFileCount = 1;
     flushAsynchronousOperations();
-    sandbox.stub(element, '_collapseAllDiffs');
+    sinon.stub(element, '_collapseAllDiffs');
     MockInteractions.tap(dom(element.root).querySelector(
         '#collapseBtn'));
     assert.isTrue(element._collapseAllDiffs.called);
   });
 
   test('show/hide diffs disabled for large amounts of files', done => {
-    const computeSpy = sandbox.spy(element, '_fileListActionsVisible');
+    const computeSpy = sinon.spy(element, '_fileListActionsVisible');
     element._files = [];
     element.changeNum = '42';
     element.basePatchNum = 'PARENT';
@@ -218,7 +215,7 @@
   });
 
   test('navigateToChange called when range select changes', () => {
-    const navigateToChangeStub = sandbox.stub(GerritNav, 'navigateToChange');
+    const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
     element.change = {
       change_id: 'Iad9dc96274af6946f3632be53b106ef80f7ba6ca',
       revisions: {
@@ -261,7 +258,7 @@
 
     test('patch specific elements', () => {
       element.editMode = true;
-      sandbox.stub(element, 'computeLatestPatchNum').returns('2');
+      sinon.stub(element, 'computeLatestPatchNum').returns('2');
       flushAsynchronousOperations();
 
       assert.isFalse(isVisible(element.$.diffPrefsContainer));
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
index 3a6cce8..051e6b3 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
@@ -46,7 +46,7 @@
 suite('gr-file-list tests', () => {
   let element;
   let commentApiWrapper;
-  let sandbox;
+
   let saveStub;
   let loadCommentSpy;
 
@@ -76,7 +76,6 @@
 
   suite('basic tests', () => {
     setup(done => {
-      sandbox = sinon.sandbox.create();
       stub('gr-rest-api-interface', {
         getLoggedIn() { return Promise.resolve(true); },
         getPreferences() { return Promise.resolve({}); },
@@ -98,13 +97,13 @@
       // comment API.
       commentApiWrapper = basicFixture.instantiate();
       element = commentApiWrapper.$.fileList;
-      loadCommentSpy = sandbox.spy(commentApiWrapper.$.commentAPI, 'loadAll');
+      loadCommentSpy = sinon.spy(commentApiWrapper.$.commentAPI, 'loadAll');
 
       // Stub methods on the changeComments object after changeComments has
       // been initialized.
       commentApiWrapper.loadComments().then(() => {
-        sandbox.stub(element.changeComments, 'getPaths').returns({});
-        sandbox.stub(element.changeComments, 'getCommentsBySideForPath')
+        sinon.stub(element.changeComments, 'getPaths').returns({});
+        sinon.stub(element.changeComments, 'getCommentsBySideForPath')
             .returns({meta: {}, left: [], right: []});
         done();
       });
@@ -115,14 +114,10 @@
         basePatchNum: 'PARENT',
         patchNum: '2',
       };
-      saveStub = sandbox.stub(element, '_saveReviewedState',
+      saveStub = sinon.stub(element, '_saveReviewedState').callsFake(
           () => Promise.resolve());
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('correct number of files are shown', () => {
       element.fileListIncrement = 300;
       element._filesByPath = Array(500).fill(0)
@@ -152,7 +147,7 @@
     });
 
     test('rendering each row calls the _reportRenderedRow method', () => {
-      const renderedStub = sandbox.stub(element, '_reportRenderedRow');
+      const renderedStub = sinon.stub(element, '_reportRenderedRow');
       element._filesByPath = Array(10).fill(0)
           .reduce((_filesByPath, _, idx) => {
             _filesByPath['/file' + idx] = {lines_inserted: 9};
@@ -602,14 +597,11 @@
       });
 
       test('toggle left diff via shortcut', () => {
-        const toggleLeftDiffStub = sandbox.stub();
+        const toggleLeftDiffStub = sinon.stub();
         // Property getter cannot be stubbed w/ sandbox due to a bug in Sinon.
         // https://github.com/sinonjs/sinon/issues/781
-        const diffsStub = sinon.stub(element, 'diffs', {
-          get() {
-            return [{toggleLeftDiff: toggleLeftDiffStub}];
-          },
-        });
+        const diffsStub = sinon.stub(element, 'diffs')
+            .get(() => [{toggleLeftDiff: toggleLeftDiffStub}]);
         MockInteractions.pressAndReleaseKeyOn(element, 65, 'shift', 'a');
         assert.isTrue(toggleLeftDiffStub.calledOnce);
         diffsStub.restore();
@@ -637,7 +629,7 @@
         assert.equal(element.selectedIndex, 1);
         MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
 
-        const navStub = sandbox.stub(GerritNav, 'navigateToDiff');
+        const navStub = sinon.stub(GerritNav, 'navigateToDiff');
         assert.equal(element.$.fileCursor.index, 2);
         assert.equal(element.selectedIndex, 2);
 
@@ -664,7 +656,7 @@
         assert.equal(element.$.fileCursor.index, 0);
         assert.equal(element.selectedIndex, 0);
 
-        const createCommentInPlaceStub = sandbox.stub(element.$.diffCursor,
+        const createCommentInPlaceStub = sinon.stub(element.$.diffCursor,
             'createCommentInPlace');
         MockInteractions.pressAndReleaseKeyOn(element, 67, null, 'c');
         assert.isTrue(createCommentInPlaceStub.called);
@@ -672,7 +664,7 @@
 
       test('i key shows/hides selected inline diff', () => {
         const paths = Object.keys(element._filesByPath);
-        sandbox.stub(element, '_expandedFilesChanged');
+        sinon.stub(element, '_expandedFilesChanged');
         flushAsynchronousOperations();
         const files = dom(element.root).querySelectorAll('.file-row');
         element.$.fileCursor.stops = files;
@@ -740,12 +732,12 @@
         let interact;
 
         setup(() => {
-          sandbox.stub(element, 'shouldSuppressKeyboardShortcut')
+          sinon.stub(element, 'shouldSuppressKeyboardShortcut')
               .returns(false);
-          sandbox.stub(element, 'modifierPressed').returns(false);
-          const openCursorStub = sandbox.stub(element, '_openCursorFile');
-          const openSelectedStub = sandbox.stub(element, '_openSelectedFile');
-          const expandStub = sandbox.stub(element, '_toggleFileExpanded');
+          sinon.stub(element, 'modifierPressed').returns(false);
+          const openCursorStub = sinon.stub(element, '_openCursorFile');
+          const openSelectedStub = sinon.stub(element, '_openSelectedFile');
+          const expandStub = sinon.stub(element, '_toggleFileExpanded');
 
           interact = function(opt_payload) {
             openCursorStub.reset();
@@ -789,11 +781,12 @@
       });
 
       test('shift+left/shift+right', () => {
-        const moveLeftStub = sandbox.stub(element.$.diffCursor, 'moveLeft');
-        const moveRightStub = sandbox.stub(element.$.diffCursor, 'moveRight');
+        const moveLeftStub = sinon.stub(element.$.diffCursor, 'moveLeft');
+        const moveRightStub = sinon.stub(element.$.diffCursor, 'moveRight');
 
         let noDiffsExpanded = true;
-        sandbox.stub(element, '_noDiffsExpanded', () => noDiffsExpanded);
+        sinon.stub(element, '_noDiffsExpanded')
+            .callsFake(() => noDiffsExpanded);
 
         MockInteractions.pressAndReleaseKeyOn(element, 73, 'shift', 'left');
         assert.isFalse(moveLeftStub.called);
@@ -833,8 +826,8 @@
         patchNum: '2',
       };
       element.$.fileCursor.setCursorAtIndex(0);
-      const reviewSpy = sandbox.spy(element, '_reviewFile');
-      const toggleExpandSpy = sandbox.spy(element, '_toggleFileExpanded');
+      const reviewSpy = sinon.spy(element, '_reviewFile');
+      const toggleExpandSpy = sinon.spy(element, '_toggleFileExpanded');
 
       flushAsynchronousOperations();
       const fileRows =
@@ -853,7 +846,7 @@
       assert.isTrue(commitReviewLabel.classList.contains('isReviewed'));
       assert.equal(markReviewLabel.textContent, 'MARK UNREVIEWED');
 
-      const clickSpy = sandbox.spy(element, '_reviewedClick');
+      const clickSpy = sinon.spy(element, '_reviewedClick');
       MockInteractions.tap(markReviewLabel);
       // assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', false));
       // assert.isFalse(commitReviewLabel.classList.contains('isReviewed'));
@@ -888,9 +881,9 @@
         patchNum: '2',
       };
 
-      const clickSpy = sandbox.spy(element, '_handleFileListClick');
-      const reviewStub = sandbox.stub(element, '_reviewFile');
-      const toggleExpandSpy = sandbox.spy(element, '_toggleFileExpanded');
+      const clickSpy = sinon.spy(element, '_handleFileListClick');
+      const reviewStub = sinon.stub(element, '_reviewFile');
+      const toggleExpandSpy = sinon.spy(element, '_toggleFileExpanded');
 
       const row = dom(element.root)
           .querySelector(`.row[data-file='{"path":"f1.txt"}']`);
@@ -924,8 +917,8 @@
       };
       element.editMode = true;
       flushAsynchronousOperations();
-      const clickSpy = sandbox.spy(element, '_handleFileListClick');
-      const toggleExpandSpy = sandbox.spy(element, '_toggleFileExpanded');
+      const clickSpy = sinon.spy(element, '_handleFileListClick');
+      const toggleExpandSpy = sinon.spy(element, '_toggleFileExpanded');
 
       // Tap the edit controls. Should be ignored by _handleFileListClick.
       MockInteractions.tap(element.shadowRoot
@@ -965,7 +958,7 @@
         patchNum: '2',
       };
       element.$.fileCursor.setCursorAtIndex(0);
-      sandbox.stub(element, '_expandedFilesChanged');
+      sinon.stub(element, '_expandedFilesChanged');
       flushAsynchronousOperations();
       const fileRows =
           dom(element.root).querySelectorAll('.row:not(.header-row)');
@@ -991,7 +984,7 @@
         basePatchNum: 'PARENT',
         patchNum: '2',
       };
-      sandbox.spy(element, '_updateDiffPreferences');
+      sinon.spy(element, '_updateDiffPreferences');
       element.$.fileCursor.setCursorAtIndex(0);
       flushAsynchronousOperations();
 
@@ -1025,14 +1018,14 @@
         basePatchNum: 'PARENT',
         patchNum: '2',
       };
-      sandbox.stub(element, '_expandedFilesChanged');
+      sinon.stub(element, '_expandedFilesChanged');
       flushAsynchronousOperations();
       const commitMsgFile = dom(element.root)
           .querySelectorAll('.row:not(.header-row) a.pathLink')[0];
 
       // Remove href attribute so the app doesn't route to a diff view
       commitMsgFile.removeAttribute('href');
-      const togglePathSpy = sandbox.spy(element, '_toggleFileExpanded');
+      const togglePathSpy = sinon.spy(element, '_toggleFileExpanded');
 
       MockInteractions.tap(commitMsgFile);
       flushAsynchronousOperations();
@@ -1047,8 +1040,8 @@
     test('_toggleFileExpanded', () => {
       const path = 'path/to/my/file.txt';
       element._filesByPath = {[path]: {}};
-      const renderSpy = sandbox.spy(element, '_renderInOrder');
-      const collapseStub = sandbox.stub(element, '_clearCollapsedDiffs');
+      const renderSpy = sinon.spy(element, '_renderInOrder');
+      const collapseStub = sinon.stub(element, '_clearCollapsedDiffs');
 
       assert.equal(element.shadowRoot
           .querySelector('iron-icon').icon, 'gr-icons:expand-more');
@@ -1072,10 +1065,10 @@
     });
 
     test('expandAllDiffs and collapseAllDiffs', () => {
-      const collapseStub = sandbox.stub(element, '_clearCollapsedDiffs');
-      const cursorUpdateStub = sandbox.stub(element.$.diffCursor,
+      const collapseStub = sinon.stub(element, '_clearCollapsedDiffs');
+      const cursorUpdateStub = sinon.stub(element.$.diffCursor,
           'handleDiffUpdate');
-      const reInitStub = sandbox.stub(element.$.diffCursor,
+      const reInitStub = sinon.stub(element.$.diffCursor,
           'reInitAndUpdateStops');
 
       const path = 'path/to/my/file.txt';
@@ -1095,7 +1088,7 @@
     });
 
     test('_expandedFilesChanged', done => {
-      sandbox.stub(element, '_reviewFile');
+      sinon.stub(element, '_reviewFile');
       const path = 'path/to/my/file.txt';
       const diffs = [{
         path,
@@ -1113,9 +1106,7 @@
           }
         },
       }];
-      sinon.stub(element, 'diffs', {
-        get() { return diffs; },
-      });
+      sinon.stub(element, 'diffs').get(() => diffs);
       element.push('_expandedFiles', {path});
     });
 
@@ -1156,7 +1147,7 @@
     });
 
     test('_renderInOrder', done => {
-      const reviewStub = sandbox.stub(element, '_reviewFile');
+      const reviewStub = sinon.stub(element, '_reviewFile');
       let callCount = 0;
       const diffs = [{
         path: 'p0',
@@ -1195,7 +1186,7 @@
 
     test('_renderInOrder logged in', done => {
       element._loggedIn = true;
-      const reviewStub = sandbox.stub(element, '_reviewFile');
+      const reviewStub = sinon.stub(element, '_reviewFile');
       let callCount = 0;
       const diffs = [{
         path: 'p0',
@@ -1237,7 +1228,7 @@
     test('_renderInOrder respects diffPrefs.manual_review', () => {
       element._loggedIn = true;
       element.diffPrefs = {manual_review: true};
-      const reviewStub = sandbox.stub(element, '_reviewFile');
+      const reviewStub = sinon.stub(element, '_reviewFile');
       const diffs = [{
         path: 'p',
         style: {},
@@ -1256,7 +1247,7 @@
     });
 
     test('_loadingChanged fired from reload in debouncer', done => {
-      sandbox.stub(element, '_getReviewedFiles').returns(Promise.resolve([]));
+      sinon.stub(element, '_getReviewedFiles').returns(Promise.resolve([]));
       element.changeNum = 123;
       element.patchRange = {patchNum: 12};
       element._filesByPath = {'foo.bar': {}};
@@ -1274,7 +1265,7 @@
     });
 
     test('_loadingChanged does not set class when there are no files', () => {
-      sandbox.stub(element, '_getReviewedFiles').returns(Promise.resolve([]));
+      sinon.stub(element, '_getReviewedFiles').returns(Promise.resolve([]));
       element.changeNum = 123;
       element.patchRange = {patchNum: 12};
       element.reload();
@@ -1286,7 +1277,7 @@
 
   suite('diff url file list', () => {
     test('diff url', () => {
-      const diffStub = sandbox.stub(GerritNav, 'getUrlForDiff')
+      const diffStub = sinon.stub(GerritNav, 'getUrlForDiff')
           .returns('/c/gerrit/+/1/1/index.php');
       const change = {
         _number: 1,
@@ -1303,7 +1294,7 @@
     });
 
     test('diff url commit msg', () => {
-      const diffStub = sandbox.stub(GerritNav, 'getUrlForDiff')
+      const diffStub = sinon.stub(GerritNav, 'getUrlForDiff')
           .returns('/c/gerrit/+/1/1//COMMIT_MSG');
       const change = {
         _number: 1,
@@ -1320,7 +1311,7 @@
     });
 
     test('edit url', () => {
-      const editStub = sandbox.stub(GerritNav, 'getEditUrlForDiff')
+      const editStub = sinon.stub(GerritNav, 'getEditUrlForDiff')
           .returns('/c/gerrit/+/1/edit/index.php,edit');
       const change = {
         _number: 1,
@@ -1337,7 +1328,7 @@
     });
 
     test('edit url commit msg', () => {
-      const editStub = sandbox.stub(GerritNav, 'getEditUrlForDiff')
+      const editStub = sinon.stub(GerritNav, 'getEditUrlForDiff')
           .returns('/c/gerrit/+/1/edit//COMMIT_MSG,edit');
       const change = {
         _number: 1,
@@ -1482,7 +1473,6 @@
 
   suite('gr-file-list inline diff tests', () => {
     let element;
-    let sandbox;
 
     const commitMsgComments = [
       {
@@ -1558,7 +1548,6 @@
     };
 
     setup(done => {
-      sandbox = sinon.sandbox.create();
       stub('gr-rest-api-interface', {
         getLoggedIn() { return Promise.resolve(true); },
         getPreferences() { return Promise.resolve({}); },
@@ -1578,15 +1567,15 @@
       // comment API.
       commentApiWrapper = basicFixture.instantiate();
       element = commentApiWrapper.$.fileList;
-      loadCommentSpy = sandbox.spy(commentApiWrapper.$.commentAPI, 'loadAll');
+      loadCommentSpy = sinon.spy(commentApiWrapper.$.commentAPI, 'loadAll');
       element.diffPrefs = {};
-      sandbox.stub(element, '_reviewFile');
+      sinon.stub(element, '_reviewFile');
 
       // Stub methods on the changeComments object after changeComments has
       // been initialized.
       commentApiWrapper.loadComments().then(() => {
-        sandbox.stub(element.changeComments, 'getPaths').returns({});
-        sandbox.stub(element.changeComments, 'getCommentsBySideForPath')
+        sinon.stub(element.changeComments, 'getPaths').returns({});
+        sinon.stub(element.changeComments, 'getCommentsBySideForPath')
             .returns({meta: {}, left: [], right: []});
         done();
       });
@@ -1615,14 +1604,10 @@
         basePatchNum: 'PARENT',
         patchNum: '2',
       };
-      sandbox.stub(window, 'fetch', () => Promise.resolve());
+      sinon.stub(window, 'fetch').callsFake(() => Promise.resolve());
       flushAsynchronousOperations();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('cursor with individually opened files', () => {
       MockInteractions.keyUpOn(element, 73, null, 'i');
       flushAsynchronousOperations();
@@ -1701,11 +1686,11 @@
       let fileRows;
 
       setup(() => {
-        sandbox.stub(element, '_renderInOrder').returns(Promise.resolve());
-        nKeySpy = sandbox.spy(element, '_handleNextChunk');
-        nextCommentStub = sandbox.stub(element.$.diffCursor,
+        sinon.stub(element, '_renderInOrder').returns(Promise.resolve());
+        nKeySpy = sinon.spy(element, '_handleNextChunk');
+        nextCommentStub = sinon.stub(element.$.diffCursor,
             'moveToNextCommentThread');
-        nextChunkStub = sandbox.stub(element.$.diffCursor,
+        nextChunkStub = sinon.stub(element.$.diffCursor,
             'moveToNextChunk');
         fileRows =
             dom(element.root).querySelectorAll('.row:not(.header-row)');
@@ -1769,7 +1754,7 @@
     test('_openSelectedFile behavior', () => {
       const _filesByPath = element._filesByPath;
       element.set('_filesByPath', {});
-      const navStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      const navStub = sinon.stub(GerritNav, 'navigateToDiff');
       // Noop when there are no files.
       element._openSelectedFile();
       assert.isFalse(navStub.called);
@@ -1782,8 +1767,10 @@
     });
 
     test('_displayLine', () => {
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut', () => false);
-      sandbox.stub(element, 'modifierPressed', () => false);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut')
+          .callsFake(() => false);
+      sinon.stub(element, 'modifierPressed')
+          .callsFake(() => false);
       element._showInlineDiffs = true;
       const mockEvent = {preventDefault() {}};
 
@@ -1803,7 +1790,7 @@
     suite('editMode behavior', () => {
       test('reviewed checkbox', () => {
         element._reviewFile.restore();
-        const saveReviewStub = sandbox.stub(element, '_saveReviewedState');
+        const saveReviewStub = sinon.stub(element, '_saveReviewedState');
 
         element.editMode = false;
         MockInteractions.pressAndReleaseKeyOn(element, 82, null, 'r');
@@ -1817,7 +1804,7 @@
       });
 
       test('_getReviewedFiles does not call API', () => {
-        const apiSpy = sandbox.spy(element.$.restAPI, 'getReviewedFiles');
+        const apiSpy = sinon.spy(element.$.restAPI, 'getReviewedFiles');
         element.editMode = true;
         return element._getReviewedFiles().then(files => {
           assert.equal(files.length, 0);
@@ -1867,7 +1854,7 @@
       assert.equal(thread2.comments[0].line, 20);
 
       const commentStub =
-          sandbox.stub(element.changeComments, 'getCommentsForThread');
+          sinon.stub(element.changeComments, 'getCommentsForThread');
       const commentStubRes1 = [
         {
           patch_set: 2,
@@ -1926,7 +1913,7 @@
       assert.equal(thread2.comments.length, 3);
 
       const commentStubCount = commentStub.callCount;
-      const getThreadsSpy = sandbox.spy(diffs[0], 'getThreadEls');
+      const getThreadsSpy = sinon.spy(diffs[0], 'getThreadEls');
 
       // Should not be getting threads when the file is not expanded.
       element.reloadCommentsForThreadWithRootId('ecf0b9fa_fe1a5f62',
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.js b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.js
index efef229..c109538 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.js
@@ -22,15 +22,11 @@
 
 suite('gr-included-in-dialog', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('_computeGroups', () => {
     const includedIn = {branches: [], tags: []};
     let filterText = '';
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js
index eb09658..0739f73 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js
@@ -23,10 +23,8 @@
 
 suite('gr-label-row-score tests', () => {
   let element;
-  let sandbox;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.labels = {
       'Code-Review': {
@@ -86,10 +84,6 @@
     flush(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   function checkAriaCheckedValid() {
     const items = element.$.labelSelector.items;
     const selectedItem = element.selectedItem;
@@ -105,7 +99,7 @@
   }
 
   test('label picker', () => {
-    const labelsChangedHandler = sandbox.stub();
+    const labelsChangedHandler = sinon.stub();
     element.addEventListener('labels-changed', labelsChangedHandler);
     assert.ok(element.$.labelSelector);
     MockInteractions.tap(element.shadowRoot
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.js b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.js
index 4a182c1..ffc17cd 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.js
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.js
@@ -22,10 +22,8 @@
 
 suite('gr-label-scores tests', () => {
   let element;
-  let sandbox;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(false); },
     });
@@ -87,10 +85,6 @@
     flush(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('get and set label scores', () => {
     for (const label in element.permittedLabels) {
       if (element.permittedLabels.hasOwnProperty(label)) {
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.js
index b9897dd..1a0969f 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.js
@@ -63,7 +63,7 @@
 suite('gr-messages-list-experimental tests', () => {
   let element;
   let messages;
-  let sandbox;
+
   let commentApiWrapper;
 
   const getMessages = function() {
@@ -140,7 +140,7 @@
         getDiffRobotComments() { return Promise.resolve({}); },
         getDiffDrafts() { return Promise.resolve({}); },
       });
-      sandbox = sinon.sandbox.create();
+
       messages = generateRandomMessages(3);
       // Element must be wrapped in an element with direct access to the
       // comment API.
@@ -153,10 +153,6 @@
       return commentApiWrapper.loadComments();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('expand/collapse all', () => {
       let allMessageEls = getMessages();
       for (const message of allMessageEls) {
@@ -211,8 +207,8 @@
         message.set('message.expanded', false);
       }
 
-      const scrollToStub = sandbox.stub(window, 'scrollTo');
-      const highlightStub = sandbox.stub(element, '_highlightEl');
+      const scrollToStub = sinon.stub(window, 'scrollTo');
+      const highlightStub = sinon.stub(element, '_highlightEl');
 
       element.scrollToMessage('invalid');
 
@@ -233,8 +229,8 @@
     });
 
     test('scroll to message offscreen', () => {
-      const scrollToStub = sandbox.stub(window, 'scrollTo');
-      const highlightStub = sandbox.stub(element, '_highlightEl');
+      const scrollToStub = sinon.stub(window, 'scrollTo');
+      const highlightStub = sinon.stub(element, '_highlightEl');
       element.messages = generateRandomMessages(25);
       flushAsynchronousOperations();
       assert.isFalse(scrollToStub.called);
@@ -442,7 +438,7 @@
   suite('gr-messages-list-experimental automate tests', () => {
     let element;
     let messages;
-    let sandbox;
+
     let commentApiWrapper;
 
     setup(() => {
@@ -454,7 +450,6 @@
         getDiffDrafts() { return Promise.resolve({}); },
       });
 
-      sandbox = sinon.sandbox.create();
       messages = [
         randomMessage(),
         randomMessage({tag: 'auto', _revision_number: 2}),
@@ -465,7 +460,7 @@
       // comment API.
       commentApiWrapper = basicFixture.instantiate();
       element = commentApiWrapper.$.messagesList;
-      sandbox.spy(commentApiWrapper.$.commentAPI, 'loadAll');
+      sinon.spy(commentApiWrapper.$.commentAPI, 'loadAll');
       element.messages = messages;
 
       // Stub methods on the changeComments object after changeComments has
@@ -473,10 +468,6 @@
       return commentApiWrapper.loadComments();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('hide autogenerated button is not hidden', () => {
       const toggle = dom(element.root).querySelector('.showAllActivityToggle');
       assert.isOk(toggle);
@@ -508,7 +499,7 @@
     });
 
     test('_computeLabelExtremes', () => {
-      const computeSpy = sandbox.spy(element, '_computeLabelExtremes');
+      const computeSpy = sinon.spy(element, '_computeLabelExtremes');
 
       element.labels = null;
       assert.isTrue(computeSpy.calledOnce);
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.js
index e3649fa..4f05dfc 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.js
@@ -87,7 +87,7 @@
 suite('gr-messages-list tests', () => {
   let element;
   let messages;
-  let sandbox;
+
   let commentApiWrapper;
 
   const getMessages = function() {
@@ -155,7 +155,7 @@
         getDiffRobotComments() { return Promise.resolve({}); },
         getDiffDrafts() { return Promise.resolve({}); },
       });
-      sandbox = sinon.sandbox.create();
+
       messages = generateRandomMessages(3);
       // Element must be wrapped in an element with direct access to the
       // comment API.
@@ -168,10 +168,6 @@
       return commentApiWrapper.loadComments();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('show some old messages', () => {
       assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
       element.messages = generateRandomMessages(26);
@@ -344,8 +340,8 @@
         message.set('message.expanded', false);
       }
 
-      const scrollToStub = sandbox.stub(window, 'scrollTo');
-      const highlightStub = sandbox.stub(element, '_highlightEl');
+      const scrollToStub = sinon.stub(window, 'scrollTo');
+      const highlightStub = sinon.stub(element, '_highlightEl');
 
       element.scrollToMessage('invalid');
 
@@ -366,8 +362,8 @@
     });
 
     test('scroll to message offscreen', () => {
-      const scrollToStub = sandbox.stub(window, 'scrollTo');
-      const highlightStub = sandbox.stub(element, '_highlightEl');
+      const scrollToStub = sinon.stub(window, 'scrollTo');
+      const highlightStub = sinon.stub(element, '_highlightEl');
       element.messages = generateRandomMessages(25);
       flushAsynchronousOperations();
       assert.isFalse(scrollToStub.called);
@@ -445,12 +441,12 @@
     test('hide increment text if increment >= total remaining', () => {
       // Test with stubbed return values, as _numRemaining and _getDelta have
       // their own tests.
-      sandbox.stub(element, '_getDelta').returns(5);
-      const remainingStub = sandbox.stub(element, '_numRemaining').returns(6);
+      sinon.stub(element, '_getDelta').returns(5);
+      const remainingStub = sinon.stub(element, '_numRemaining').returns(6);
       assert.isFalse(element._computeIncrementHidden(null, null, null));
       remainingStub.restore();
 
-      sandbox.stub(element, '_numRemaining').returns(4);
+      sinon.stub(element, '_numRemaining').returns(4);
       assert.isTrue(element._computeIncrementHidden(null, null, null));
     });
   });
@@ -458,7 +454,7 @@
   suite('gr-messages-list automate tests', () => {
     let element;
     let messages;
-    let sandbox;
+
     let commentApiWrapper;
 
     const getMessages = function() {
@@ -482,7 +478,6 @@
         getDiffDrafts() { return Promise.resolve({}); },
       });
 
-      sandbox = sinon.sandbox.create();
       messages = generateRandomAutomatedMessages(2);
       messages.push(randomMessageReviewer);
 
@@ -490,7 +485,7 @@
       // comment API.
       commentApiWrapper = basicFixture.instantiate();
       element = commentApiWrapper.$.messagesList;
-      sandbox.spy(commentApiWrapper.$.commentAPI, 'loadAll');
+      sinon.spy(commentApiWrapper.$.commentAPI, 'loadAll');
       element.messages = messages;
 
       // Stub methods on the changeComments object after changeComments has
@@ -498,10 +493,6 @@
       return commentApiWrapper.loadComments();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('hide autogenerated button is not hidden', () => {
       assert.isNotOk(element.shadowRoot
           .querySelector('#automatedMessageToggle[hidden]'));
@@ -572,7 +563,7 @@
     });
 
     test('initially show only 20 messages', () => {
-      sandbox.stub(element.reporting, 'reportInteraction',
+      sinon.stub(element.reporting, 'reportInteraction').callsFake(
           (eventName, details) => {
             assert.equal(typeof(eventName), 'string');
             if (details) {
@@ -589,7 +580,7 @@
     });
 
     test('_computeLabelExtremes', () => {
-      const computeSpy = sandbox.spy(element, '_computeLabelExtremes');
+      const computeSpy = sinon.spy(element, '_computeLabelExtremes');
 
       element.labels = null;
       assert.isTrue(computeSpy.calledOnce);
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js
index 9f652d1..4c164ba 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js
@@ -27,15 +27,9 @@
 
 suite('gr-related-changes-list tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('connected revisions', () => {
@@ -247,7 +241,7 @@
   });
 
   test('event for section loaded fires for each section ', () => {
-    const loadedStub = sandbox.stub();
+    const loadedStub = sinon.stub();
     element.patchNum = 7;
     element.change = {
       change_id: 123,
@@ -255,13 +249,13 @@
     };
     element.mergeable = true;
     element.addEventListener('new-section-loaded', loadedStub);
-    sandbox.stub(element, '_getRelatedChanges')
+    sinon.stub(element, '_getRelatedChanges')
         .returns(Promise.resolve({changes: []}));
-    sandbox.stub(element, '_getSubmittedTogether')
+    sinon.stub(element, '_getSubmittedTogether')
         .returns(Promise.resolve());
-    sandbox.stub(element, '_getCherryPicks')
+    sinon.stub(element, '_getCherryPicks')
         .returns(Promise.resolve());
-    sandbox.stub(element, '_getConflicts')
+    sinon.stub(element, '_getConflicts')
         .returns(Promise.resolve());
 
     return element.reload().then(() => {
@@ -275,13 +269,13 @@
     setup(() => {
       element = basicFixture.instantiate();
 
-      sandbox.stub(element, '_getRelatedChanges')
+      sinon.stub(element, '_getRelatedChanges')
           .returns(Promise.resolve({changes: []}));
-      sandbox.stub(element, '_getSubmittedTogether')
+      sinon.stub(element, '_getSubmittedTogether')
           .returns(Promise.resolve());
-      sandbox.stub(element, '_getCherryPicks')
+      sinon.stub(element, '_getCherryPicks')
           .returns(Promise.resolve());
-      sandbox.stub(element, '_getConflicts')
+      sinon.stub(element, '_getConflicts')
           .returns(Promise.resolve());
     });
 
@@ -304,13 +298,13 @@
     setup(() => {
       element = basicFixture.instantiate();
 
-      sandbox.stub(element, '_getRelatedChanges')
+      sinon.stub(element, '_getRelatedChanges')
           .returns(Promise.resolve({changes: []}));
-      sandbox.stub(element, '_getSubmittedTogether')
+      sinon.stub(element, '_getSubmittedTogether')
           .returns(Promise.resolve());
-      sandbox.stub(element, '_getCherryPicks')
+      sinon.stub(element, '_getCherryPicks')
           .returns(Promise.resolve());
-      conflictsStub = sandbox.stub(element, '_getConflicts')
+      conflictsStub = sinon.stub(element, '_getConflicts')
           .returns(Promise.resolve());
     });
 
@@ -408,7 +402,7 @@
     });
 
     test('update fires', () => {
-      const updateHandler = sandbox.stub();
+      const updateHandler = sinon.stub();
       element.addEventListener('update', updateHandler);
 
       element._resultsChanged({}, {}, [], [], []);
@@ -471,7 +465,7 @@
   });
 
   test('_computeChangeURL uses GerritNav', () => {
-    const getUrlStub = sandbox.stub(GerritNav, 'getUrlForChangeById');
+    const getUrlStub = sinon.stub(GerritNav, 'getUrlForChangeById');
     element._computeChangeURL(123, 'abc/def', 12);
     assert.isTrue(getUrlStub.called);
   });
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js
index a2f51cb..9a94d27 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js
@@ -30,8 +30,6 @@
   let changeNum;
   let patchNum;
 
-  let sandbox;
-
   const setupElement = element => {
     element.change = {
       _number: changeNum,
@@ -70,13 +68,11 @@
         '+1',
       ],
     };
-    sandbox.stub(element, 'fetchChangeUpdates')
+    sinon.stub(element, 'fetchChangeUpdates')
         .returns(Promise.resolve({isLatest: true}));
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     changeNum = 42;
     patchNum = 1;
 
@@ -93,15 +89,14 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     resetPlugins();
   });
 
   test('_submit blocked when invalid email is supplied to ccs', () => {
-    const sendStub = sandbox.stub(element, 'send').returns(Promise.resolve());
+    const sendStub = sinon.stub(element, 'send').returns(Promise.resolve());
     // Stub the below function to avoid side effects from the send promise
     // resolving.
-    sandbox.stub(element, '_purgeReviewersPendingRemove');
+    sinon.stub(element, '_purgeReviewersPendingRemove');
 
     element.$.ccs.$.entry.setText('test');
     MockInteractions.tap(element.shadowRoot
@@ -131,7 +126,8 @@
     }, null, 'http://test.com/plugins/lgtm.js');
     element = basicFixture.instantiate();
     setupElement(element);
-    sandbox.stub(pluginEndpoints, 'importUrl', url => Promise.resolve());
+    sinon.stub(pluginEndpoints, 'importUrl')
+        .callsFake( url => Promise.resolve());
     pluginLoader.loadPlugins([]);
     pluginLoader.awaitPluginsLoaded().then(() => {
       flush(() => {
@@ -148,4 +144,4 @@
       });
     });
   });
-});
\ No newline at end of file
+});
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.js
index 7262fc3..4c65298 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.js
@@ -48,7 +48,6 @@
   let changeNum;
   let patchNum;
 
-  let sandbox;
   let getDraftCommentStub;
   let setDraftCommentStub;
   let eraseDraftCommentStub;
@@ -58,8 +57,6 @@
   const makeGroup = function() { return {id: lastId++}; };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     changeNum = 42;
     patchNum = 1;
 
@@ -70,7 +67,7 @@
       getChangeSuggestedReviewers() { return Promise.resolve([]); },
     });
 
-    sandbox.stub(appContext.flagsService, 'isEnabled').returns(true);
+    sinon.stub(appContext.flagsService, 'isEnabled').returns(true);
 
     element = basicFixture.instantiate();
     element.change = {
@@ -113,27 +110,23 @@
       ],
     };
 
-    getDraftCommentStub = sandbox.stub(element.$.storage, 'getDraftComment');
-    setDraftCommentStub = sandbox.stub(element.$.storage, 'setDraftComment');
-    eraseDraftCommentStub = sandbox.stub(element.$.storage,
+    getDraftCommentStub = sinon.stub(element.$.storage, 'getDraftComment');
+    setDraftCommentStub = sinon.stub(element.$.storage, 'setDraftComment');
+    eraseDraftCommentStub = sinon.stub(element.$.storage,
         'eraseDraftComment');
 
-    sandbox.stub(element, 'fetchChangeUpdates')
+    sinon.stub(element, 'fetchChangeUpdates')
         .returns(Promise.resolve({isLatest: true}));
 
     // Allow the elements created by dom-repeat to be stamped.
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   function stubSaveReview(jsonResponseProducer) {
-    return sandbox.stub(
+    return sinon.stub(
         element,
-        '_saveReview',
-        review => new Promise((resolve, reject) => {
+        '_saveReview')
+        .callsFake(review => new Promise((resolve, reject) => {
           try {
             const result = jsonResponseProducer(review) || {};
             const resultStr =
@@ -327,7 +320,7 @@
       });
     });
 
-    sandbox.stub(element.$.labelScores, 'getLabelValues', () => {
+    sinon.stub(element.$.labelScores, 'getLabelValues').callsFake( () => {
       return {
         'Code-Review': -1,
         'Verified': -1,
@@ -635,7 +628,7 @@
   });
 
   test('400 converts to human-readable server-error', done => {
-    sandbox.stub(window, 'fetch', () => {
+    sinon.stub(window, 'fetch').callsFake(() => {
       const text = '....{"reviewers":{"id1":{"error":"first error"}},' +
         '"ccs":{"id2":{"error":"second error"}}}';
       return Promise.resolve(cloneableResponse(400, text));
@@ -657,7 +650,7 @@
   });
 
   test('non-json 400 is treated as a normal server-error', done => {
-    sandbox.stub(window, 'fetch', () => {
+    sinon.stub(window, 'fetch').callsFake(() => {
       const text = 'Comment validation error!';
       return Promise.resolve(cloneableResponse(400, text));
     });
@@ -707,12 +700,12 @@
   });
 
   test('_focusOn', () => {
-    sandbox.spy(element, '_chooseFocusTarget');
+    sinon.spy(element, '_chooseFocusTarget');
     flushAsynchronousOperations();
-    const textareaStub = sandbox.stub(element.$.textarea, 'async');
-    const reviewerEntryStub = sandbox.stub(element.$.reviewers.focusStart,
+    const textareaStub = sinon.stub(element.$.textarea, 'async');
+    const reviewerEntryStub = sinon.stub(element.$.reviewers.focusStart,
         'async');
-    const ccStub = sandbox.stub(element.$.ccs.focusStart, 'async');
+    const ccStub = sinon.stub(element.$.ccs.focusStart, 'async');
     element._focusOn();
     assert.equal(element._chooseFocusTarget.callCount, 1);
     assert.deepEqual(textareaStub.callCount, 1);
@@ -811,7 +804,7 @@
   });
 
   test('_purgeReviewersPendingRemove', () => {
-    const removeStub = sandbox.stub(element, '_removeAccount');
+    const removeStub = sinon.stub(element, '_removeAccount');
     const mock = function() {
       element._reviewersPendingRemove = {
         test: [makeAccount()],
@@ -836,7 +829,7 @@
   });
 
   test('_removeAccount', done => {
-    sandbox.stub(element.$.restAPI, 'removeChangeReviewer')
+    sinon.stub(element.$.restAPI, 'removeChangeReviewer')
         .returns(Promise.resolve({ok: true}));
     const arr = [makeAccount(), makeAccount()];
     element.change.reviewers = {
@@ -937,7 +930,7 @@
 
     stubSaveReview(review => mutations.push(...review.reviewers));
 
-    sandbox.stub(element, '_removeAccount', (account, type) => {
+    sinon.stub(element, '_removeAccount').callsFake((account, type) => {
       mutations.push({state: 'REMOVED', account});
       return Promise.resolve();
     });
@@ -1007,7 +1000,7 @@
   });
 
   test('emits cancel on esc key', () => {
-    const cancelHandler = sandbox.spy();
+    const cancelHandler = sinon.spy();
     element.addEventListener('cancel', cancelHandler);
     MockInteractions.pressAndReleaseKeyOn(element, 27, null, 'esc');
     flushAsynchronousOperations();
@@ -1115,10 +1108,10 @@
     let startReviewStub;
 
     setup(() => {
-      startReviewStub = sandbox.stub(
+      startReviewStub = sinon.stub(
           element.$.restAPI,
-          'startReview',
-          () => Promise.resolve());
+          'startReview')
+          .callsFake(() => Promise.resolve());
     });
 
     test('ready property in review input on start review', () => {
@@ -1145,7 +1138,7 @@
     let sendStub;
 
     setup(() => {
-      sendStub = sandbox.stub(element, 'send', () => Promise.resolve());
+      sendStub = sinon.stub(element, 'send').callsFake(() => Promise.resolve());
       element.canBeStarted = true;
       // Flush to make both Start/Save buttons appear in DOM.
       flushAsynchronousOperations();
@@ -1201,10 +1194,10 @@
     suite('pending diff drafts?', () => {
       test('yes', () => {
         const promise = mockPromise();
-        const refreshHandler = sandbox.stub();
+        const refreshHandler = sinon.stub();
 
         element.addEventListener('comment-refresh', refreshHandler);
-        sandbox.stub(element.$.restAPI, 'hasPendingDiffDrafts').returns(true);
+        sinon.stub(element.$.restAPI, 'hasPendingDiffDrafts').returns(true);
         element.$.restAPI._pendingRequests.sendDiffDraft = [promise];
         element.open();
 
@@ -1220,7 +1213,7 @@
       });
 
       test('no', () => {
-        sandbox.stub(element.$.restAPI, 'hasPendingDiffDrafts').returns(false);
+        sinon.stub(element.$.restAPI, 'hasPendingDiffDrafts').returns(false);
         element.open();
         assert.notOk(element._savingComments);
       });
@@ -1358,10 +1351,10 @@
   });
 
   test('_submit blocked when no mutations exist', () => {
-    const sendStub = sandbox.stub(element, 'send').returns(Promise.resolve());
+    const sendStub = sinon.stub(element, 'send').returns(Promise.resolve());
     // Stub the below function to avoid side effects from the send promise
     // resolving.
-    sandbox.stub(element, '_purgeReviewersPendingRemove');
+    sinon.stub(element, '_purgeReviewersPendingRemove');
     element.draftCommentThreads = [];
     flushAsynchronousOperations();
 
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.js
index f9b32cd..809a768 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.js
@@ -23,12 +23,11 @@
 
 suite('gr-reviewer-list tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
     element.serverConfig = {};
-    sandbox = sinon.sandbox.create();
+
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
       removeChangeReviewer() {
@@ -37,10 +36,6 @@
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('controls hidden on immutable element', () => {
     element.mutable = false;
     assert.isTrue(element.shadowRoot
@@ -162,7 +157,7 @@
   });
 
   test('_handleAddTap passes mode with event', () => {
-    const fireStub = sandbox.stub(element, 'dispatchEvent');
+    const fireStub = sinon.stub(element, 'dispatchEvent');
     const e = {preventDefault() {}};
 
     element.ccsOnly = false;
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js
index 67af168..df4850c 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js
@@ -25,7 +25,7 @@
 
 suite('gr-thread-list tests', () => {
   let element;
-  let sandbox;
+
   let threadElements;
 
   function getVisibleThreads() {
@@ -35,7 +35,6 @@
   }
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.threads = [
       {
@@ -268,10 +267,6 @@
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('draft toggle only appears when logged in', () => {
     assert.equal(getComputedStyle(element.shadowRoot
         .querySelector('.draftToggle')).display,
@@ -579,8 +574,8 @@
   });
 
   test('modification events are consumed and displatched', () => {
-    sandbox.spy(element, '_handleCommentsChanged');
-    const dispatchSpy = sandbox.stub();
+    sinon.spy(element, '_handleCommentsChanged');
+    const dispatchSpy = sinon.stub();
     element.addEventListener('thread-list-modified', dispatchSpy);
     threadElements[0].dispatchEvent(
         new CustomEvent('thread-changed', {
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
index 934e822..934f244 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
@@ -26,25 +26,18 @@
 
 suite('gr-error-manager tests', () => {
   let element;
-  let sandbox;
-
-  setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
-  });
 
   suite('when authed', () => {
     let toastSpy;
+    let openOverlaySpy;
 
     setup(() => {
-      sandbox.stub(window, 'fetch')
+      sinon.stub(window, 'fetch')
           .returns(Promise.resolve({ok: true, status: 204}));
       element = basicFixture.instantiate();
       element._authService.clearCache();
-      toastSpy = sandbox.spy(element, '_createToastAlert');
+      toastSpy = sinon.spy(element, '_createToastAlert');
+      openOverlaySpy = sinon.spy(element.$.noInteractionOverlay, 'open');
     });
 
     teardown(() => {
@@ -54,7 +47,7 @@
     });
 
     test('does not show auth error on 403 by default', done => {
-      const showAuthErrorStub = sandbox.stub(element, '_showAuthErrorAlert');
+      const showAuthErrorStub = sinon.stub(element, '_showAuthErrorAlert');
       const responseText = Promise.resolve('server says no.');
       element.dispatchEvent(
           new CustomEvent('server-error', {
@@ -70,7 +63,7 @@
 
     test('show auth required for 403 with auth error and not authed before',
         done => {
-          const showAuthErrorStub = sandbox.stub(
+          const showAuthErrorStub = sinon.stub(
               element, '_showAuthErrorAlert'
           );
           const responseText = Promise.resolve('Authentication required\n');
@@ -107,7 +100,7 @@
     });
 
     test('show logged in error', () => {
-      sandbox.stub(element, '_showAuthErrorAlert');
+      sinon.stub(element, '_showAuthErrorAlert');
       element.dispatchEvent(
           new CustomEvent('show-auth-required', {
             composed: true, bubbles: true,
@@ -117,8 +110,8 @@
     });
 
     test('show normal Error', done => {
-      const showErrorStub = sandbox.stub(element, '_showErrorDialog');
-      const textSpy = sandbox.spy(() => Promise.resolve('ZOMG'));
+      const showErrorStub = sinon.stub(element, '_showErrorDialog');
+      const textSpy = sinon.spy(() => Promise.resolve('ZOMG'));
       element.dispatchEvent(
           new CustomEvent('server-error', {
             detail: {response: {status: 500, text: textSpy}},
@@ -165,7 +158,7 @@
     });
 
     test('extract trace id from headers if exists', done => {
-      const textSpy = sandbox.spy(
+      const textSpy = sinon.spy(
           () => Promise.resolve('500')
       );
       const headers = new Headers();
@@ -191,8 +184,8 @@
     });
 
     test('suppress TOO_MANY_FILES error', done => {
-      const showAlertStub = sandbox.stub(element, '_showAlert');
-      const textSpy = sandbox.spy(
+      const showAlertStub = sinon.stub(element, '_showAlert');
+      const textSpy = sinon.spy(
           () => Promise.resolve('too many files to find conflicts')
       );
       element.dispatchEvent(
@@ -209,8 +202,8 @@
     });
 
     test('show network error', done => {
-      const consoleErrorStub = sandbox.stub(console, 'error');
-      const showAlertStub = sandbox.stub(element, '_showAlert');
+      const consoleErrorStub = sinon.stub(console, 'error');
+      const showAlertStub = sinon.stub(element, '_showAlert');
       element.dispatchEvent(
           new CustomEvent('network-error', {
             detail: {error: new Error('ZOMG')},
@@ -226,12 +219,12 @@
       });
     });
 
-    test('show auth refresh toast', done => {
+    test('show auth refresh toast', async () => {
       // starts with authed state
       element.$.restAPI.getLoggedIn();
-      const refreshStub = sandbox.stub(element.$.restAPI, 'getAccount',
+      const refreshStub = sinon.stub(element.$.restAPI, 'getAccount').callsFake(
           () => Promise.resolve({}));
-      const windowOpen = sandbox.stub(window, 'open');
+      const windowOpen = sinon.stub(window, 'open');
       const responseText = Promise.resolve('Authentication required\n');
       // fake failed auth
       window.fetch.returns(Promise.resolve({status: 403}));
@@ -242,64 +235,63 @@
             composed: true, bubbles: true,
           }));
       assert.equal(window.fetch.callCount, 1);
-      flush(() => {
-        // here needs two flush as there are two chanined
-        // promises on server-error handler and flush only flushes one
-        assert.equal(window.fetch.callCount, 2);
-        flush(() => {
-          // auth-error fired
-          assert.isTrue(toastSpy.called);
+      await flush();
 
-          // toast
-          let toast = toastSpy.lastCall.returnValue;
-          assert.isOk(toast);
-          assert.include(
-              dom(toast.root).textContent, 'Credentials expired.');
-          assert.include(
-              dom(toast.root).textContent, 'Refresh credentials');
+      // here needs two flush as there are two chanined
+      // promises on server-error handler and flush only flushes one
+      assert.equal(window.fetch.callCount, 2);
+      await flush();
+      // Sometime overlay opens with delay, waiting while open is complete
+      await openOverlaySpy.lastCall.returnValue;
+      // auth-error fired
+      assert.isTrue(toastSpy.called);
 
-          // noInteractionOverlay
-          const noInteractionOverlay = element.$.noInteractionOverlay;
-          assert.isOk(noInteractionOverlay);
-          sinon.spy(noInteractionOverlay, 'close');
-          assert.equal(
-              noInteractionOverlay.backdropElement.getAttribute('opened'),
-              '');
-          assert.isFalse(windowOpen.called);
-          MockInteractions.tap(toast.shadowRoot
-              .querySelector('gr-button.action'));
-          assert.isTrue(windowOpen.called);
+      // toast
+      let toast = toastSpy.lastCall.returnValue;
+      assert.isOk(toast);
+      assert.include(
+          dom(toast.root).textContent, 'Credentials expired.');
+      assert.include(
+          dom(toast.root).textContent, 'Refresh credentials');
 
-          // @see Issue 5822: noopener breaks closeAfterLogin
-          assert.equal(windowOpen.lastCall.args[2].indexOf('noopener=yes'),
-              -1);
+      // noInteractionOverlay
+      const noInteractionOverlay = element.$.noInteractionOverlay;
+      assert.isOk(noInteractionOverlay);
+      sinon.spy(noInteractionOverlay, 'close');
+      assert.equal(
+          noInteractionOverlay.backdropElement.getAttribute('opened'),
+          '');
+      assert.isFalse(windowOpen.called);
+      MockInteractions.tap(toast.shadowRoot
+          .querySelector('gr-button.action'));
+      assert.isTrue(windowOpen.called);
 
-          const hideToastSpy = sandbox.spy(toast, 'hide');
+      // @see Issue 5822: noopener breaks closeAfterLogin
+      assert.equal(windowOpen.lastCall.args[2].indexOf('noopener=yes'),
+          -1);
 
-          // now fake authed
-          window.fetch.returns(Promise.resolve({status: 204}));
-          element._handleWindowFocus();
-          element.flushDebouncer('checkLoggedIn');
-          flush(() => {
-            assert.isTrue(refreshStub.called);
-            assert.isTrue(hideToastSpy.called);
+      const hideToastSpy = sinon.spy(toast, 'hide');
 
-            // toast update
-            assert.notStrictEqual(toastSpy.lastCall.returnValue, toast);
-            toast = toastSpy.lastCall.returnValue;
-            assert.isOk(toast);
-            assert.include(
-                dom(toast.root).textContent, 'Credentials refreshed');
+      // now fake authed
+      window.fetch.returns(Promise.resolve({status: 204}));
+      element._handleWindowFocus();
+      element.flushDebouncer('checkLoggedIn');
+      await flush();
+      assert.isTrue(refreshStub.called);
+      assert.isTrue(hideToastSpy.called);
 
-            // close overlay
-            assert.isTrue(noInteractionOverlay.close.called);
-            done();
-          });
-        });
-      });
+      // toast update
+      assert.notStrictEqual(toastSpy.lastCall.returnValue, toast);
+      toast = toastSpy.lastCall.returnValue;
+      assert.isOk(toast);
+      assert.include(
+          dom(toast.root).textContent, 'Credentials refreshed');
+
+      // close overlay
+      assert.isTrue(noInteractionOverlay.close.called);
     });
 
-    test('auth toast should dismiss existing toast', done => {
+    test('auth toast should dismiss existing toast', async () => {
       // starts with authed state
       element.$.restAPI.getLoggedIn();
       const responseText = Promise.resolve('Authentication required\n');
@@ -310,7 +302,7 @@
             detail: {message: 'test reload', action: 'reload'},
             composed: true, bubbles: true,
           }));
-      const toast = toastSpy.lastCall.returnValue;
+      let toast = toastSpy.lastCall.returnValue;
       assert.isOk(toast);
       assert.include(
           dom(toast.root).textContent, 'test reload');
@@ -324,20 +316,19 @@
             composed: true, bubbles: true,
           }));
       assert.equal(window.fetch.callCount, 1);
-      flush(() => {
-        // here needs two flush as there are two chained
-        // promises on server-error handler and flush only flushes one
-        assert.equal(window.fetch.callCount, 2);
-        flush(() => {
-          // toast
-          const toast = toastSpy.lastCall.returnValue;
-          assert.include(
-              dom(toast.root).textContent, 'Credentials expired.');
-          assert.include(
-              dom(toast.root).textContent, 'Refresh credentials');
-          done();
-        });
-      });
+      await flush();
+      // here needs two flush as there are two chained
+      // promises on server-error handler and flush only flushes one
+      assert.equal(window.fetch.callCount, 2);
+      await flush();
+      // Sometime overlay opens with delay, waiting while open is complete
+      await openOverlaySpy.lastCall.returnValue;
+      // toast
+      toast = toastSpy.lastCall.returnValue;
+      assert.include(
+          dom(toast.root).textContent, 'Credentials expired.');
+      assert.include(
+          dom(toast.root).textContent, 'Refresh credentials');
     });
 
     test('regular toast should dismiss regular toast', () => {
@@ -412,7 +403,7 @@
 
     test('show alert', () => {
       const alertObj = {message: 'foo'};
-      sandbox.stub(element, '_showAlert');
+      sinon.stub(element, '_showAlert');
       element.dispatchEvent(
           new CustomEvent('show-alert', {
             detail: alertObj,
@@ -425,9 +416,9 @@
     });
 
     test('checks stale credentials on visibility change', () => {
-      const refreshStub = sandbox.stub(element,
+      const refreshStub = sinon.stub(element,
           '_checkSignedIn');
-      sandbox.stub(Date, 'now').returns(999999);
+      sinon.stub(Date, 'now').returns(999999);
       element._lastCredentialCheck = 0;
       element._handleVisibilityChange();
 
@@ -445,12 +436,12 @@
 
     test('refreshes with same credentials', done => {
       const accountPromise = Promise.resolve({_account_id: 1234});
-      sandbox.stub(element.$.restAPI, 'getAccount')
+      sinon.stub(element.$.restAPI, 'getAccount')
           .returns(accountPromise);
-      const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
-      const handleRefreshStub = sandbox.stub(element,
+      const requestCheckStub = sinon.stub(element, '_requestCheckLoggedIn');
+      const handleRefreshStub = sinon.stub(element,
           '_handleCredentialRefreshed');
-      const reloadStub = sandbox.stub(element, '_reloadPage');
+      const reloadStub = sinon.stub(element, '_reloadPage');
 
       element.knownAccountId = 1234;
       element._refreshingCredentials = true;
@@ -466,15 +457,15 @@
 
     test('_showAlert hides existing alerts', () => {
       element._alertElement = element._createToastAlert();
-      const hideStub = sandbox.stub(element, '_hideAlert');
+      const hideStub = sinon.stub(element, '_hideAlert');
       element._showAlert();
       assert.isTrue(hideStub.calledOnce);
     });
 
     test('show-error', () => {
-      const openStub = sandbox.stub(element.$.errorOverlay, 'open');
-      const closeStub = sandbox.stub(element.$.errorOverlay, 'close');
-      const reportStub = sandbox.stub(
+      const openStub = sinon.stub(element.$.errorOverlay, 'open');
+      const closeStub = sinon.stub(element.$.errorOverlay, 'close');
+      const reportStub = sinon.stub(
           element.reporting,
           'reportErrorDialog'
       );
@@ -502,14 +493,14 @@
 
     test('reloads when refreshed credentials differ', done => {
       const accountPromise = Promise.resolve({_account_id: 1234});
-      sandbox.stub(element.$.restAPI, 'getAccount')
+      sinon.stub(element.$.restAPI, 'getAccount')
           .returns(accountPromise);
-      const requestCheckStub = sandbox.stub(
+      const requestCheckStub = sinon.stub(
           element,
           '_requestCheckLoggedIn');
-      const handleRefreshStub = sandbox.stub(element,
+      const handleRefreshStub = sinon.stub(element,
           '_handleCredentialRefreshed');
-      const reloadStub = sandbox.stub(element, '_reloadPage');
+      const reloadStub = sinon.stub(element, '_reloadPage');
 
       element.knownAccountId = 4321; // Different from 1234
       element._refreshingCredentials = true;
@@ -531,7 +522,7 @@
         getLoggedIn() { return Promise.resolve(false); },
       });
       element = basicFixture.instantiate();
-      toastSpy = sandbox.spy(element, '_createToastAlert');
+      toastSpy = sinon.spy(element, '_createToastAlert');
     });
 
     teardown(() => {
@@ -541,12 +532,12 @@
     });
 
     test('refresh loop continues on credential fail', done => {
-      const requestCheckStub = sandbox.stub(
+      const requestCheckStub = sinon.stub(
           element,
           '_requestCheckLoggedIn');
-      const handleRefreshStub = sandbox.stub(element,
+      const handleRefreshStub = sinon.stub(element,
           '_handleCredentialRefreshed');
-      const reloadStub = sandbox.stub(element, '_reloadPage');
+      const reloadStub = sinon.stub(element, '_reloadPage');
 
       element._refreshingCredentials = true;
       element._checkSignedIn();
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
index 00e781b..eeeb86b 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
@@ -59,15 +59,17 @@
   /** @override */
   attached() {
     super.attached();
+    this.keyboardShortcutDirectoryListener =
+        this._onDirectoryUpdated.bind(this);
     this.addKeyboardShortcutDirectoryListener(
-        this._onDirectoryUpdated.bind(this));
+        this.keyboardShortcutDirectoryListener);
   }
 
   /** @override */
   detached() {
     super.detached();
     this.removeKeyboardShortcutDirectoryListener(
-        this._onDirectoryUpdated.bind(this));
+        this.keyboardShortcutDirectoryListener);
   }
 
   _handleCloseTap(e) {
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.js
index bbff1d8..b3ac40f 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.js
@@ -22,10 +22,8 @@
 
 suite('gr-main-header tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
       probePath(path) { return Promise.resolve(false); },
@@ -36,10 +34,6 @@
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('link visibility', () => {
     element.loading = true;
     assert.equal(getComputedStyle(element.shadowRoot
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js
index 6f3692c..b53b269 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js
@@ -24,15 +24,11 @@
 
 suite('gr-router tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('_firstCodeBrowserWeblink', () => {
     assert.deepEqual(element._firstCodeBrowserWeblink([
       {name: 'gitweb'},
@@ -50,7 +46,7 @@
     const link = {name: 'test', url: 'test/url'};
     const weblinks = [browserLink, link];
     const config = {gerrit: {primary_weblink_name: browserLink.name}};
-    sandbox.stub(element, '_firstCodeBrowserWeblink').returns(link);
+    sinon.stub(element, '_firstCodeBrowserWeblink').returns(link);
 
     assert.deepEqual(element._getBrowseCommitWeblink(weblinks, config),
         browserLink);
@@ -62,7 +58,7 @@
     const link = {name: 'test', url: 'test/url'};
     const browserLink = {name: 'browser', url: 'browser/url'};
     const mapLinksToConfig = weblinks => { return {options: {weblinks}}; };
-    sandbox.stub(element, '_getBrowseCommitWeblink').returns(browserLink);
+    sinon.stub(element, '_getBrowseCommitWeblink').returns(browserLink);
 
     assert.deepEqual(
         element._getChangeWeblinks(mapLinksToConfig([link, browserLink]))[0],
@@ -137,16 +133,17 @@
 
     const requiresAuth = {};
     const doesNotRequireAuth = {};
-    sandbox.stub(GerritNav, 'setup');
-    sandbox.stub(page, 'start');
-    sandbox.stub(page, 'base');
-    sandbox.stub(element, '_mapRoute', (pattern, methodName, usesAuth) => {
-      if (usesAuth) {
-        requiresAuth[methodName] = true;
-      } else {
-        doesNotRequireAuth[methodName] = true;
-      }
-    });
+    sinon.stub(GerritNav, 'setup');
+    sinon.stub(page, 'start');
+    sinon.stub(page, 'base');
+    sinon.stub(element, '_mapRoute').callsFake(
+        (pattern, methodName, usesAuth) => {
+          if (usesAuth) {
+            requiresAuth[methodName] = true;
+          } else {
+            doesNotRequireAuth[methodName] = true;
+          }
+        });
     element._startRouter();
 
     const actualRequiresAuth = Object.keys(requiresAuth);
@@ -227,19 +224,19 @@
   });
 
   test('_redirectIfNotLoggedIn while logged in', () => {
-    sandbox.stub(element.$.restAPI, 'getLoggedIn')
+    sinon.stub(element.$.restAPI, 'getLoggedIn')
         .returns(Promise.resolve(true));
     const data = {canonicalPath: ''};
-    const redirectStub = sandbox.stub(element, '_redirectToLogin');
+    const redirectStub = sinon.stub(element, '_redirectToLogin');
     return element._redirectIfNotLoggedIn(data).then(() => {
       assert.isFalse(redirectStub.called);
     });
   });
 
   test('_redirectIfNotLoggedIn while logged out', () => {
-    sandbox.stub(element.$.restAPI, 'getLoggedIn')
+    sinon.stub(element.$.restAPI, 'getLoggedIn')
         .returns(Promise.resolve(false));
-    const redirectStub = sandbox.stub(element, '_redirectToLogin');
+    const redirectStub = sinon.stub(element, '_redirectToLogin');
     const data = {canonicalPath: ''};
     return new Promise(resolve => {
       element._redirectIfNotLoggedIn(data)
@@ -520,9 +517,9 @@
     let projectLookupStub;
 
     setup(() => {
-      projectLookupStub = sandbox
+      projectLookupStub = sinon
           .stub(element.$.restAPI, 'getFromProjectLookup');
-      sandbox.stub(element, '_generateUrl');
+      sinon.stub(element, '_generateUrl');
     });
 
     suite('_normalizeLegacyRouteParams', () => {
@@ -531,10 +528,10 @@
       let show404Stub;
 
       setup(() => {
-        rangeStub = sandbox.stub(element, '_normalizePatchRangeParams')
+        rangeStub = sinon.stub(element, '_normalizePatchRangeParams')
             .returns(Promise.resolve());
-        redirectStub = sandbox.stub(element, '_redirect');
-        show404Stub = sandbox.stub(element, '_show404');
+        redirectStub = sinon.stub(element, '_redirect');
+        show404Stub = sinon.stub(element, '_show404');
       });
 
       test('w/o changeNum', () => {
@@ -607,9 +604,9 @@
     }
 
     setup(() => {
-      redirectStub = sandbox.stub(element, '_redirect');
-      setParamsStub = sandbox.stub(element, '_setParams');
-      handlePassThroughRoute = sandbox.stub(element, '_handlePassThroughRoute');
+      redirectStub = sinon.stub(element, '_redirect');
+      setParamsStub = sinon.stub(element, '_setParams');
+      handlePassThroughRoute = sinon.stub(element, '_handlePassThroughRoute');
     });
 
     test('_handleAgreementsRoute', () => {
@@ -664,10 +661,10 @@
       const onRegisteringExit = (match, _onExit) => {
         onExit = _onExit;
       };
-      sandbox.stub(page, 'exit', onRegisteringExit);
-      sandbox.stub(GerritNav, 'setup');
-      sandbox.stub(page, 'start');
-      sandbox.stub(page, 'base');
+      sinon.stub(page, 'exit').callsFake( onRegisteringExit);
+      sinon.stub(GerritNav, 'setup');
+      sinon.stub(page, 'start');
+      sinon.stub(page, 'base');
       element._startRouter();
 
       const appElementStub = {dispatchEvent: sinon.stub()};
@@ -689,7 +686,7 @@
           redirectStub.lastCall.args[0],
           '/c/test/+/42');
 
-      sandbox.stub(element, '_getHashFromCanonicalPath').returns('foo');
+      sinon.stub(element, '_getHashFromCanonicalPath').returns('foo');
       element._handleImproperlyEncodedPlusRoute(
           {canonicalPath: '/c/test/%20/42', params: ['test', '42']});
       assert.equal(
@@ -764,7 +761,7 @@
     suite('_handleRootRoute', () => {
       test('closes for closeAfterLogin', () => {
         const data = {querystring: 'closeAfterLogin', canonicalPath: ''};
-        const closeStub = sandbox.stub(window, 'close');
+        const closeStub = sinon.stub(window, 'close');
         const result = element._handleRootRoute(data);
         assert.isNotOk(result);
         assert.isTrue(closeStub.called);
@@ -772,7 +769,7 @@
       });
 
       test('redirects to dashboard if logged in', () => {
-        sandbox.stub(element.$.restAPI, 'getLoggedIn')
+        sinon.stub(element.$.restAPI, 'getLoggedIn')
             .returns(Promise.resolve(true));
         const data = {
           canonicalPath: '/', path: '/', querystring: '', hash: '',
@@ -785,7 +782,7 @@
       });
 
       test('redirects to open changes if not logged in', () => {
-        sandbox.stub(element.$.restAPI, 'getLoggedIn')
+        sinon.stub(element.$.restAPI, 'getLoggedIn')
             .returns(Promise.resolve(false));
         const data = {
           canonicalPath: '/', path: '/', querystring: '', hash: '',
@@ -841,7 +838,7 @@
             querystring: '',
             hash: '/foo/bar',
           };
-          sandbox.stub(element, 'getBaseUrl').returns('/baz');
+          sinon.stub(element, 'getBaseUrl').returns('/baz');
           const result = element._handleRootRoute(data);
           assert.isNotOk(result);
           assert.isTrue(redirectStub.called);
@@ -879,11 +876,11 @@
       let redirectToLoginStub;
 
       setup(() => {
-        redirectToLoginStub = sandbox.stub(element, '_redirectToLogin');
+        redirectToLoginStub = sinon.stub(element, '_redirectToLogin');
       });
 
       test('own dashboard but signed out redirects to login', () => {
-        sandbox.stub(element.$.restAPI, 'getLoggedIn')
+        sinon.stub(element.$.restAPI, 'getLoggedIn')
             .returns(Promise.resolve(false));
         const data = {canonicalPath: '/dashboard/', params: {0: 'seLF'}};
         return element._handleDashboardRoute(data, '').then(() => {
@@ -894,7 +891,7 @@
       });
 
       test('non-self dashboard but signed out does not redirect', () => {
-        sandbox.stub(element.$.restAPI, 'getLoggedIn')
+        sinon.stub(element.$.restAPI, 'getLoggedIn')
             .returns(Promise.resolve(false));
         const data = {canonicalPath: '/dashboard/', params: {0: 'foo'}};
         return element._handleDashboardRoute(data, '').then(() => {
@@ -906,7 +903,7 @@
       });
 
       test('dashboard while signed in sets params', () => {
-        sandbox.stub(element.$.restAPI, 'getLoggedIn')
+        sinon.stub(element.$.restAPI, 'getLoggedIn')
             .returns(Promise.resolve(true));
         const data = {canonicalPath: '/dashboard/', params: {0: 'foo'}};
         return element._handleDashboardRoute(data, '').then(() => {
@@ -925,7 +922,7 @@
       let redirectToLoginStub;
 
       setup(() => {
-        redirectToLoginStub = sandbox.stub(element, '_redirectToLogin');
+        redirectToLoginStub = sinon.stub(element, '_redirectToLogin');
       });
 
       test('no user specified', () => {
@@ -1332,7 +1329,7 @@
       });
 
       test('_handleChangeLegacyRoute', () => {
-        const normalizeRouteStub = sandbox.stub(element,
+        const normalizeRouteStub = sinon.stub(element,
             '_normalizeLegacyRouteParams');
         const ctx = {
           params: [
@@ -1357,7 +1354,7 @@
       });
 
       test('_handleDiffLegacyRoute', () => {
-        const normalizeRouteStub = sandbox.stub(element,
+        const normalizeRouteStub = sinon.stub(element,
             '_normalizeLegacyRouteParams');
         const ctx = {
           params: [
@@ -1420,14 +1417,14 @@
         }
 
         setup(() => {
-          normalizeRangeStub = sandbox.stub(element,
+          normalizeRangeStub = sinon.stub(element,
               '_normalizePatchRangeParams');
-          sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+          sinon.stub(element.$.restAPI, 'setInProjectLookup');
         });
 
         test('needs redirect', () => {
           normalizeRangeStub.returns(true);
-          sandbox.stub(element, '_generateUrl').returns('foo');
+          sinon.stub(element, '_generateUrl').returns('foo');
           const ctx = makeParams(null, '');
           element._handleChangeRoute(ctx);
           assert.isTrue(normalizeRangeStub.called);
@@ -1438,7 +1435,7 @@
 
         test('change view', () => {
           normalizeRangeStub.returns(false);
-          sandbox.stub(element, '_generateUrl').returns('foo');
+          sinon.stub(element, '_generateUrl').returns('foo');
           const ctx = makeParams(null, '');
           assertDataToParams(ctx, '_handleChangeRoute', {
             view: GerritNav.View.CHANGE,
@@ -1474,14 +1471,14 @@
         }
 
         setup(() => {
-          normalizeRangeStub = sandbox.stub(element,
+          normalizeRangeStub = sinon.stub(element,
               '_normalizePatchRangeParams');
-          sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+          sinon.stub(element.$.restAPI, 'setInProjectLookup');
         });
 
         test('needs redirect', () => {
           normalizeRangeStub.returns(true);
-          sandbox.stub(element, '_generateUrl').returns('foo');
+          sinon.stub(element, '_generateUrl').returns('foo');
           const ctx = makeParams(null, '');
           element._handleDiffRoute(ctx);
           assert.isTrue(normalizeRangeStub.called);
@@ -1492,7 +1489,7 @@
 
         test('diff view', () => {
           normalizeRangeStub.returns(false);
-          sandbox.stub(element, '_generateUrl').returns('foo');
+          sinon.stub(element, '_generateUrl').returns('foo');
           const ctx = makeParams('foo/bar/baz', 'b44');
           assertDataToParams(ctx, '_handleDiffRoute', {
             view: GerritNav.View.DIFF,
@@ -1511,8 +1508,8 @@
 
       test('_handleDiffEditRoute', () => {
         const normalizeRangeSpy =
-            sandbox.spy(element, '_normalizePatchRangeParams');
-        sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+            sinon.spy(element, '_normalizePatchRangeParams');
+        sinon.stub(element.$.restAPI, 'setInProjectLookup');
         const ctx = {
           params: [
             'foo/bar', // 0 Project
@@ -1540,8 +1537,8 @@
 
       test('_handleDiffEditRoute with lineNum', () => {
         const normalizeRangeSpy =
-            sandbox.spy(element, '_normalizePatchRangeParams');
-        sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+            sinon.spy(element, '_normalizePatchRangeParams');
+        sinon.stub(element.$.restAPI, 'setInProjectLookup');
         const ctx = {
           params: [
             'foo/bar', // 0 Project
@@ -1570,8 +1567,8 @@
 
       test('_handleChangeEditRoute', () => {
         const normalizeRangeSpy =
-            sandbox.spy(element, '_normalizePatchRangeParams');
-        sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+            sinon.spy(element, '_normalizePatchRangeParams');
+        sinon.stub(element.$.restAPI, 'setInProjectLookup');
         const ctx = {
           params: [
             'foo/bar', // 0 Project
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.js
index 61ecc3c..9529f9b 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.js
@@ -24,7 +24,6 @@
 
 suite('gr-search-bar tests', () => {
   let element;
-  let sandbox;
 
   suiteSetup(() => {
     const kb = TestKeyboardShortcutBinder.push();
@@ -36,15 +35,10 @@
   });
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     flush(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('value is propagated to _inputVal', () => {
     element.value = 'foo';
     assert.equal(element._inputVal, 'foo');
@@ -66,7 +60,7 @@
   });
 
   test('input blurred after commit', () => {
-    const blurSpy = sandbox.spy(element.$.searchInput.$.input, 'blur');
+    const blurSpy = sinon.spy(element.$.searchInput.$.input, 'blur');
     element.$.searchInput.text = 'fate/stay';
     MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
         null, 'enter');
@@ -74,7 +68,7 @@
   });
 
   test('empty search query does not trigger nav', () => {
-    const searchSpy = sandbox.spy();
+    const searchSpy = sinon.spy();
     element.addEventListener('handle-search', searchSpy);
     element.value = '';
     MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
@@ -83,7 +77,7 @@
   });
 
   test('Predefined query op with no predication doesnt trigger nav', () => {
-    const searchSpy = sandbox.spy();
+    const searchSpy = sinon.spy();
     element.addEventListener('handle-search', searchSpy);
     element.value = 'added:';
     MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
@@ -92,7 +86,7 @@
   });
 
   test('predefined predicate query triggers nav', () => {
-    const searchSpy = sandbox.spy();
+    const searchSpy = sinon.spy();
     element.addEventListener('handle-search', searchSpy);
     element.value = 'age:1week';
     MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
@@ -101,7 +95,7 @@
   });
 
   test('undefined predicate query triggers nav', () => {
-    const searchSpy = sandbox.spy();
+    const searchSpy = sinon.spy();
     element.addEventListener('handle-search', searchSpy);
     element.value = 'random:1week';
     MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
@@ -110,7 +104,7 @@
   });
 
   test('empty undefined predicate query triggers nav', () => {
-    const searchSpy = sandbox.spy();
+    const searchSpy = sinon.spy();
     element.addEventListener('handle-search', searchSpy);
     element.value = 'random:';
     MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
@@ -119,8 +113,8 @@
   });
 
   test('keyboard shortcuts', () => {
-    const focusSpy = sandbox.spy(element.$.searchInput, 'focus');
-    const selectAllSpy = sandbox.spy(element.$.searchInput, 'selectAll');
+    const focusSpy = sinon.spy(element.$.searchInput, 'focus');
+    const selectAllSpy = sinon.spy(element.$.searchInput, 'selectAll');
     MockInteractions.pressAndReleaseKeyOn(document.body, 191, null, '/');
     assert.isTrue(focusSpy.called);
     assert.isTrue(selectAllSpy.called);
@@ -128,7 +122,7 @@
 
   suite('_getSearchSuggestions', () => {
     test('Autocompletes accounts', () => {
-      sandbox.stub(element, 'accountSuggestions', () =>
+      sinon.stub(element, 'accountSuggestions').callsFake(() =>
         Promise.resolve([{text: 'owner:fred@goog.co'}])
       );
       return element._getSearchSuggestions('owner:fr').then(s => {
@@ -137,7 +131,7 @@
     });
 
     test('Autocompletes groups', done => {
-      sandbox.stub(element, 'groupSuggestions', () =>
+      sinon.stub(element, 'groupSuggestions').callsFake(() =>
         Promise.resolve([
           {text: 'ownerin:Polygerrit'},
           {text: 'ownerin:gerrit'},
@@ -150,7 +144,7 @@
     });
 
     test('Autocompletes projects', done => {
-      sandbox.stub(element, 'projectSuggestions', () =>
+      sinon.stub(element, 'projectSuggestions').callsFake(() =>
         Promise.resolve([
           {text: 'project:Polygerrit'},
           {text: 'project:gerrit'},
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js
index 74063f0..dc7bf0e 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js
@@ -22,19 +22,13 @@
 
 suite('gr-smart-search tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('Autocompletes accounts', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedAccounts').callsFake(() =>
       Promise.resolve([
         {
           name: 'fred',
@@ -48,7 +42,7 @@
   });
 
   test('Inserts self as option when valid', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedAccounts').callsFake( () =>
       Promise.resolve([
         {
           name: 'fred',
@@ -68,7 +62,7 @@
   });
 
   test('Inserts me as option when valid', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedAccounts').callsFake( () =>
       Promise.resolve([
         {
           name: 'fred',
@@ -88,7 +82,7 @@
   });
 
   test('Autocompletes groups', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedGroups').callsFake( () =>
       Promise.resolve({
         Polygerrit: 0,
         gerrit: 0,
@@ -101,7 +95,7 @@
   });
 
   test('Autocompletes projects', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedProjects', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedProjects').callsFake( () =>
       Promise.resolve({Polygerrit: 0}));
     return element._fetchProjects('project', 'pol').then(s => {
       assert.deepEqual(s[0], {text: 'project:Polygerrit'});
@@ -109,7 +103,7 @@
   });
 
   test('Autocomplete doesnt override exact matches to input', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedGroups').callsFake( () =>
       Promise.resolve({
         Polygerrit: 0,
         gerrit: 0,
@@ -124,7 +118,7 @@
   });
 
   test('Autocompletes accounts with no email', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedAccounts').callsFake( () =>
       Promise.resolve([{name: 'fred'}]));
     return element._fetchAccounts('owner', 'fr').then(s => {
       assert.deepEqual(s[0], {text: 'owner:"fred"', label: 'fred'});
@@ -132,7 +126,7 @@
   });
 
   test('Autocompletes accounts with email', () => {
-    sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+    sinon.stub(element.$.restAPI, 'getSuggestedAccounts').callsFake( () =>
       Promise.resolve([{email: 'fred@goog.co'}]));
     return element._fetchAccounts('owner', 'fr').then(s => {
       assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: ''});
diff --git a/polygerrit-ui/app/elements/custom-dark-theme_test.js b/polygerrit-ui/app/elements/custom-dark-theme_test.js
index bb91214..d4f790f 100644
--- a/polygerrit-ui/app/elements/custom-dark-theme_test.js
+++ b/polygerrit-ui/app/elements/custom-dark-theme_test.js
@@ -25,18 +25,16 @@
 const basicFixture = fixtureFromElement('gr-app');
 
 suite('gr-app custom dark theme tests', () => {
-  let sandbox;
   let element;
   setup(done => {
     window.localStorage.setItem('dark-theme', 'true');
-    sandbox = sinon.sandbox.create();
+
     element = basicFixture.instantiate();
     pluginLoader.loadPlugins([]);
     pluginLoader.awaitPluginsLoaded().then(() => flush(done));
   });
 
   teardown(() => {
-    sandbox.restore();
     window.localStorage.removeItem('dark-theme');
     removeTheme();
     // The app sends requests to server. This can lead to
diff --git a/polygerrit-ui/app/elements/custom-light-theme_test.js b/polygerrit-ui/app/elements/custom-light-theme_test.js
index 3ba602b..5c0cf28 100644
--- a/polygerrit-ui/app/elements/custom-light-theme_test.js
+++ b/polygerrit-ui/app/elements/custom-light-theme_test.js
@@ -24,10 +24,8 @@
 const basicFixture = fixtureFromElement('gr-app');
 
 suite('gr-app custom light theme tests', () => {
-  let sandbox;
   let element;
   setup(done => {
-    sandbox = sinon.sandbox.create();
     window.localStorage.removeItem('dark-theme');
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({test: 'config'}); },
@@ -42,7 +40,6 @@
     pluginLoader.awaitPluginsLoaded().then(() => flush(done));
   });
   teardown(() => {
-    sandbox.restore();
     // The app sends requests to server. This can lead to
     // unexpected gr-alert elements in document.body
     document.body.querySelectorAll('gr-alert').forEach(grAlert => {
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js
index c9f1660..b3ba637 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js
@@ -23,7 +23,7 @@
 
 suite('gr-apply-fix-dialog tests', () => {
   let element;
-  let sandbox;
+
   const ROBOT_COMMENT_WITH_TWO_FIXES = {
     robot_id: 'robot_1',
     fix_suggestions: [{fix_id: 'fix_1'}, {fix_id: 'fix_2'}],
@@ -35,7 +35,6 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.changeNum = '1';
     element._patchNum = 2;
@@ -55,13 +54,9 @@
     };
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('dialog open', () => {
     setup(() => {
-      sandbox.stub(element.$.restAPI, 'getRobotCommentFixPreview')
+      sinon.stub(element.$.restAPI, 'getRobotCommentFixPreview')
           .returns(Promise.resolve({
             f1: {
               meta_a: {},
@@ -94,7 +89,7 @@
               ],
             },
           }));
-      sandbox.stub(element.$.applyFixOverlay, 'open')
+      sinon.stub(element.$.applyFixOverlay, 'open')
           .returns(Promise.resolve());
     });
 
@@ -152,9 +147,9 @@
   });
 
   test('next button state updated when suggestions changed', done => {
-    sandbox.stub(element.$.restAPI, 'getRobotCommentFixPreview')
+    sinon.stub(element.$.restAPI, 'getRobotCommentFixPreview')
         .returns(Promise.resolve({}));
-    sandbox.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
+    sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
 
     element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_ONE_FIX}})
         .then(() => assert.isTrue(element.$.nextFix.disabled))
@@ -168,7 +163,7 @@
   });
 
   test('preview endpoint throws error should reset dialog', done => {
-    sandbox.stub(window, 'fetch', (url => {
+    sinon.stub(window, 'fetch').callsFake((url => {
       if (url.endsWith('/preview')) {
         return Promise.reject(new Error('backend error'));
       }
@@ -191,9 +186,9 @@
 
   test('apply fix button should call apply ' +
   'and navigate to change view', done => {
-    sandbox.stub(element.$.restAPI, 'applyFixSuggestion')
+    sinon.stub(element.$.restAPI, 'applyFixSuggestion')
         .returns(Promise.resolve({ok: true}));
-    sandbox.stub(GerritNav, 'navigateToChange');
+    sinon.stub(GerritNav, 'navigateToChange');
     element._currentFix = {fix_id: '123'};
 
     element._handleApplyFix().then(() => {
@@ -217,9 +212,9 @@
   });
 
   test('should not navigate to change view if incorect reponse', done => {
-    sandbox.stub(element.$.restAPI, 'applyFixSuggestion')
+    sinon.stub(element.$.restAPI, 'applyFixSuggestion')
         .returns(Promise.resolve({}));
-    sandbox.stub(GerritNav, 'navigateToChange');
+    sinon.stub(GerritNav, 'navigateToChange');
     element._currentFix = {fix_id: '123'};
 
     element._handleApplyFix().then(() => {
@@ -233,7 +228,7 @@
   });
 
   test('select fix forward and back of multiple suggested fixes', done => {
-    sandbox.stub(element.$.restAPI, 'getRobotCommentFixPreview')
+    sinon.stub(element.$.restAPI, 'getRobotCommentFixPreview')
         .returns(Promise.resolve({
           f1: {
             meta_a: {},
@@ -266,7 +261,7 @@
             ],
           },
         }));
-    sandbox.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
+    sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
 
     element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_TWO_FIXES}})
         .then(() => {
@@ -279,7 +274,7 @@
   });
 
   test('server-error should throw for failed apply call', done => {
-    sandbox.stub(window, 'fetch', (url => {
+    sinon.stub(window, 'fetch').callsFake((url => {
       if (url.endsWith('/apply')) {
         return Promise.reject(new Error('backend error'));
       }
@@ -291,7 +286,7 @@
     }));
     const errorStub = sinon.stub();
     document.addEventListener('network-error', errorStub);
-    sandbox.stub(GerritNav, 'navigateToChange');
+    sinon.stub(GerritNav, 'navigateToChange');
     element._currentFix = {fix_id: '123'};
     element._handleApplyFix();
     flush(() => {
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.js
index dc599c7..0a7c3b5 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.js
@@ -24,27 +24,23 @@
   const PARENT = 'PARENT';
 
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('loads logged-out', () => {
     const changeNum = 1234;
 
-    sandbox.stub(element.$.restAPI, 'getLoggedIn')
+    sinon.stub(element.$.restAPI, 'getLoggedIn')
         .returns(Promise.resolve(false));
-    sandbox.stub(element.$.restAPI, 'getDiffComments')
+    sinon.stub(element.$.restAPI, 'getDiffComments')
         .returns(Promise.resolve({
           'foo.c': [{id: '123', message: 'foo bar', in_reply_to: '321'}],
         }));
-    sandbox.stub(element.$.restAPI, 'getDiffRobotComments')
+    sinon.stub(element.$.restAPI, 'getDiffRobotComments')
         .returns(Promise.resolve({'foo.c': [{id: '321', message: 'done'}]}));
-    sandbox.stub(element.$.restAPI, 'getDiffDrafts')
+    sinon.stub(element.$.restAPI, 'getDiffDrafts')
         .returns(Promise.resolve({}));
 
     return element.loadAll(changeNum).then(() => {
@@ -63,15 +59,15 @@
   test('loads logged-in', () => {
     const changeNum = 1234;
 
-    sandbox.stub(element.$.restAPI, 'getLoggedIn')
+    sinon.stub(element.$.restAPI, 'getLoggedIn')
         .returns(Promise.resolve(true));
-    sandbox.stub(element.$.restAPI, 'getDiffComments')
+    sinon.stub(element.$.restAPI, 'getDiffComments')
         .returns(Promise.resolve({
           'foo.c': [{id: '123', message: 'foo bar', in_reply_to: '321'}],
         }));
-    sandbox.stub(element.$.restAPI, 'getDiffRobotComments')
+    sinon.stub(element.$.restAPI, 'getDiffRobotComments')
         .returns(Promise.resolve({'foo.c': [{id: '321', message: 'done'}]}));
-    sandbox.stub(element.$.restAPI, 'getDiffDrafts')
+    sinon.stub(element.$.restAPI, 'getDiffDrafts')
         .returns(Promise.resolve({'foo.c': [{id: '555', message: 'ack'}]}));
 
     return element.loadAll(changeNum).then(() => {
@@ -92,17 +88,17 @@
     let robotCommentStub;
     let draftStub;
     setup(() => {
-      commentStub = sandbox.stub(element.$.restAPI, 'getDiffComments')
+      commentStub = sinon.stub(element.$.restAPI, 'getDiffComments')
           .returns(Promise.resolve({}));
-      robotCommentStub = sandbox.stub(element.$.restAPI,
+      robotCommentStub = sinon.stub(element.$.restAPI,
           'getDiffRobotComments').returns(Promise.resolve({}));
-      draftStub = sandbox.stub(element.$.restAPI, 'getDiffDrafts')
+      draftStub = sinon.stub(element.$.restAPI, 'getDiffDrafts')
           .returns(Promise.resolve({}));
     });
 
     test('without loadAll first', done => {
       assert.isNotOk(element._changeComments);
-      sandbox.spy(element, 'loadAll');
+      sinon.spy(element, 'loadAll');
       element.reloadDrafts().then(() => {
         assert.isTrue(element.loadAll.called);
         assert.isOk(element._changeComments);
@@ -195,9 +191,9 @@
       const patchRange2 = {basePatchNum: 123, patchNum: 125};
       const patchRange3 = {basePatchNum: 124, patchNum: 125};
 
-      const isInBasePatchStub = sandbox.stub(element._changeComments,
+      const isInBasePatchStub = sinon.stub(element._changeComments,
           '_isInBaseOfPatchRange');
-      const isInRevisionPatchStub = sandbox.stub(element._changeComments,
+      const isInRevisionPatchStub = sinon.stub(element._changeComments,
           '_isInRevisionOfPatchRange');
 
       isInBasePatchStub.withArgs({}, patchRange1).returns(true);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
index c988f3a..a88ac27 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
@@ -53,11 +53,10 @@
   let prefs;
   let element;
   let builder;
-  let sandbox;
+
   const LINE_FEED_HTML = '<span class="style-scope gr-diff br"></span>';
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(false); },
@@ -71,8 +70,6 @@
     builder = new GrDiffBuilder({content: []}, prefs);
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('_createElement classStr applies all classes', () => {
     const node = builder._createElement('div', 'test classes');
     assert.isTrue(node.classList.contains('gr-diff'));
@@ -303,7 +300,7 @@
   });
 
   test('_handlePreferenceError called with invalid preference', () => {
-    sandbox.stub(element, '_handlePreferenceError');
+    sinon.stub(element, '_handlePreferenceError');
     const prefs = {tab_size: 0};
     element._getDiffBuilder(element.diff, prefs);
     assert.isTrue(element._handlePreferenceError.lastCall
@@ -365,7 +362,7 @@
     setup(() => {
       el = divWithTextFixture.instantiate();
       str = el.textContent;
-      annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
+      annotateElementSpy = sinon.spy(GrAnnotation, 'annotateElement');
       layer = document.createElement('gr-diff-builder')
           ._createIntralineLayer();
     });
@@ -530,7 +527,7 @@
       const line = {text: ''};
       const el = document.createElement('div');
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
 
       layer.annotate(el, lineNumberEl, line);
 
@@ -543,7 +540,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
 
       layer.annotate(el, lineNumberEl, line);
 
@@ -556,7 +553,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
 
       layer.annotate(el, lineNumberEl, line);
 
@@ -576,7 +573,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
 
       layer.annotate(el, lineNumberEl, line);
 
@@ -589,7 +586,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
 
       layer.annotate(el, lineNumberEl, line);
 
@@ -614,7 +611,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
 
       layer.annotate(el, lineNumberEl, line);
 
@@ -678,7 +675,7 @@
       const line = {text: ''};
       const el = document.createElement('div');
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isFalse(annotateElementStub.called);
     });
@@ -689,7 +686,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isFalse(annotateElementStub.called);
     });
@@ -700,7 +697,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isTrue(annotateElementStub.called);
       assert.equal(annotateElementStub.lastCall.args[1], 11);
@@ -713,7 +710,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isTrue(annotateElementStub.called);
       assert.equal(annotateElementStub.lastCall.args[1], 11);
@@ -726,7 +723,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isTrue(annotateElementStub.called);
       assert.equal(annotateElementStub.lastCall.args[1], 11);
@@ -739,7 +736,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isTrue(annotateElementStub.called);
       assert.equal(annotateElementStub.lastCall.args[1], 1);
@@ -753,7 +750,7 @@
       const el = document.createElement('div');
       el.textContent = str;
       const annotateElementStub =
-          sandbox.stub(GrAnnotation, 'annotateElement');
+          sinon.stub(GrAnnotation, 'annotateElement');
       layer.annotate(el, lineNumberEl, line);
       assert.isFalse(annotateElementStub.called);
     });
@@ -768,7 +765,7 @@
     setup(() => {
       element = basicFixture.instantiate();
       element.viewMode = 'SIDE_BY_SIDE';
-      processStub = sandbox.stub(element.$.processor, 'process')
+      processStub = sinon.stub(element.$.processor, 'process')
           .returns(Promise.resolve());
       keyLocations = {left: {}, right: {}};
       prefs = {
@@ -843,9 +840,9 @@
       element = basicFixture.instantiate();
       outputEl = element.queryEffectiveChildren('#diffTable');
       keyLocations = {left: {}, right: {}};
-      sandbox.stub(element, '_getDiffBuilder', () => {
+      sinon.stub(element, '_getDiffBuilder').callsFake(() => {
         const builder = new GrDiffBuilder({content}, prefs, outputEl);
-        sandbox.stub(builder, 'addColumns');
+        sinon.stub(builder, 'addColumns');
         builder.buildSectionElement = function(group) {
           const section = document.createElement('stub');
           section.textContent = group.lines
@@ -882,7 +879,7 @@
     });
 
     test('render-start and render-content are fired', done => {
-      const dispatchEventStub = sandbox.stub(element, 'dispatchEvent');
+      const dispatchEventStub = sinon.stub(element, 'dispatchEvent');
       element.render(keyLocations, {}).then(() => {
         const firedEventTypes = dispatchEventStub.getCalls()
             .map(c => c.args[0].type);
@@ -893,7 +890,7 @@
     });
 
     test('cancel', () => {
-      const processorCancelStub = sandbox.stub(element.$.processor, 'cancel');
+      const processorCancelStub = sinon.stub(element.$.processor, 'cancel');
       element.cancel();
       assert.isTrue(processorCancelStub.called);
     });
@@ -998,7 +995,7 @@
     });
 
     test('_renderContentByRange', () => {
-      const spy = sandbox.spy(builder, '_createTextEl');
+      const spy = sinon.spy(builder, '_createTextEl');
       const start = 9;
       const end = 14;
       const count = end - start + 1;
@@ -1012,9 +1009,9 @@
     });
 
     test('_renderContentByRange notexistent elements', () => {
-      const spy = sandbox.spy(builder, '_createTextEl');
+      const spy = sinon.spy(builder, '_createTextEl');
 
-      sandbox.stub(builder, 'findLinesByRange',
+      sinon.stub(builder, 'findLinesByRange').callsFake(
           (s, e, d, lines, elements) => {
             // Add a line and a corresponding element.
             lines.push(new GrDiffLine(GrDiffLine.Type.BOTH));
@@ -1170,7 +1167,7 @@
     });
 
     test('setBlame attempts to render each blamed line', () => {
-      const getBlameStub = sandbox.stub(builder, '_getBlameByLineNum')
+      const getBlameStub = sinon.stub(builder, '_getBlameByLineNum')
           .returns(null);
       builder.setBlame(mockBlame);
       assert.equal(getBlameStub.callCount, 32);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js
index 9301d64..4ca75eb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js
@@ -32,13 +32,10 @@
 const emptyFixture = fixtureFromElement('div');
 
 suite('gr-diff-cursor tests', () => {
-  let sandbox;
   let cursorElement;
   let diffElement;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
-
     const fixtureElems = basicFixture.instantiate();
     diffElement = fixtureElems[0];
     cursorElement = fixtureElems[1];
@@ -68,8 +65,6 @@
     });
   });
 
-  teardown(() => sandbox.restore());
-
   test('diff cursor functionality (side-by-side)', () => {
     // The cursor has been initialized to the first delta.
     assert.isOk(cursorElement.diffRow);
@@ -119,7 +114,7 @@
   });
 
   test('moves to selected line', () => {
-    const moveToNumStub = sandbox.stub(cursorElement, 'moveToLineNumber');
+    const moveToNumStub = sinon.stub(cursorElement, 'moveToLineNumber');
 
     cursorElement._handleDiffLineSelected(
         new CustomEvent('line-selected', {
@@ -232,7 +227,7 @@
   test('navigate to next unreviewed file via moveToNextChunk', () => {
     const cursor = cursorElement.shadowRoot.querySelector('#cursorManager');
     cursor.index = cursor.stops.length - 1;
-    const dispatchEventStub = sandbox.stub(cursor, 'dispatchEvent');
+    const dispatchEventStub = sinon.stub(cursor, 'dispatchEvent');
     cursorElement.moveToNextChunk(/* opt_clipToTop = */false,
         /* opt_navigateToNextFile = */true);
     assert.isTrue(dispatchEventStub.called);
@@ -246,9 +241,10 @@
 
   test('initialLineNumber not provided', done => {
     let scrollBehaviorDuringMove;
-    const moveToNumStub = sandbox.stub(cursorElement, 'moveToLineNumber');
-    const moveToChunkStub = sandbox.stub(cursorElement, 'moveToFirstChunk',
-        () => { scrollBehaviorDuringMove = cursorElement._scrollMode; });
+    const moveToNumStub = sinon.stub(cursorElement, 'moveToLineNumber');
+    const moveToChunkStub = sinon.stub(cursorElement, 'moveToFirstChunk')
+        .callsFake(
+            () => { scrollBehaviorDuringMove = cursorElement._scrollMode; });
 
     function renderHandler() {
       diffElement.removeEventListener('render', renderHandler);
@@ -265,9 +261,10 @@
 
   test('initialLineNumber provided', done => {
     let scrollBehaviorDuringMove;
-    const moveToNumStub = sandbox.stub(cursorElement, 'moveToLineNumber',
-        () => { scrollBehaviorDuringMove = cursorElement._scrollMode; });
-    const moveToChunkStub = sandbox.stub(cursorElement, 'moveToFirstChunk');
+    const moveToNumStub = sinon.stub(cursorElement, 'moveToLineNumber')
+        .callsFake(
+            () => { scrollBehaviorDuringMove = cursorElement._scrollMode; });
+    const moveToChunkStub = sinon.stub(cursorElement, 'moveToFirstChunk');
     function renderHandler() {
       diffElement.removeEventListener('render', renderHandler);
       cursorElement.reInitCursor();
@@ -349,9 +346,9 @@
     });
 
     test('createCommentInPlace ignores call if nothing is selected', () => {
-      const createRangeCommentStub = sandbox.stub(diffElement,
+      const createRangeCommentStub = sinon.stub(diffElement,
           'createRangeComment');
-      const addDraftAtLineStub = sandbox.stub(diffElement, 'addDraftAtLine');
+      const addDraftAtLineStub = sinon.stub(diffElement, 'addDraftAtLine');
       cursorElement.diffRow = undefined;
       cursorElement.createCommentInPlace();
       assert.isFalse(createRangeCommentStub.called);
@@ -398,7 +395,7 @@
   });
 
   test('expand context updates stops', done => {
-    sandbox.spy(cursorElement, '_updateStops');
+    sinon.spy(cursorElement, '_updateStops');
     MockInteractions.tap(diffElement.shadowRoot
         .querySelector('.showContext'));
     flush(() => {
@@ -408,15 +405,13 @@
   });
 
   suite('gr-diff-cursor event tests', () => {
-    let sandbox;
     let someEmptyDiv;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       someEmptyDiv = emptyFixture.instantiate();
     });
 
-    teardown(() => sandbox.restore());
+    teardown(() => sinon.restore());
 
     test('ready is fired after component is rendered', done => {
       const cursorElement = document.createElement('gr-diff-cursor');
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.js
index 3c107dd..65f5e07 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.js
@@ -28,19 +28,13 @@
   let str;
   let parent;
   let textNode;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     parent = basicFixture.instantiate();
     textNode = parent.childNodes[0];
     str = textNode.textContent;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_annotateText Case 1', () => {
     GrAnnotation._annotateText(textNode, 0, str.length, 'foobar');
 
@@ -194,7 +188,7 @@
     setup(() => {
       originalSanitizeDOMValue = sanitizeDOMValue;
       assert.isDefined(originalSanitizeDOMValue);
-      mockSanitize = sandbox.spy(originalSanitizeDOMValue);
+      mockSanitize = sinon.spy(originalSanitizeDOMValue);
       setSanitizeDOMValue(mockSanitize);
     });
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js
index 1695988..957d039 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js
@@ -47,10 +47,10 @@
           <tr class="diff-row side-by-side" left-type="remove" right-type="add">
             <td class="left lineNum" data-value="2"></td>
             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
-            <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">\\u0009</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">\\u0009</span>quod <hl>Epicurum</hl></div></td>
+            <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">\u0009</span>quod <hl>Epicurum</hl></div></td>
             <td class="right lineNum" data-value="2"></td>
             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
-            <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">\\u0009</span></hl> otiosum,  <span class="tab-indicator" style="tab-size:8;">\\u0009</span> audiam,  sit, quod</div></td>
+            <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl> otiosum,  <span class="tab-indicator" style="tab-size:8;">\u0009</span> audiam,  sit, quod</div></td>
           </tr>
         </tbody>
 
@@ -67,19 +67,19 @@
           <tr class="diff-row side-by-side" left-type="remove" right-type="add">
             <td class="left lineNum" data-value="140"></td>
             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
-            <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">\\u0009</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">\\u0009</span>quod <hl>Epicurum</hl></div><div class="comment-thread">
+            <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">\u0009</span>quod <hl>Epicurum</hl></div><div class="comment-thread">
                 [Yet another random diff thread content here]
             </div></td>
             <td class="right lineNum" data-value="120"></td>
             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
-            <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">\\u0009</span></hl> otiosum,  <span class="tab-indicator" style="tab-size:8;">\\u0009</span> audiam,  sit, quod</div></td>
+            <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl> otiosum,  <span class="tab-indicator" style="tab-size:8;">\u0009</span> audiam,  sit, quod</div></td>
           </tr>
         </tbody>
 
         <tbody class="section both">
           <tr class="diff-row side-by-side" left-type="both" right-type="both">
             <td class="left lineNum" data-value="141"></td>
-            <td class="content both"><div class="contentText">nam et<hl><span class="tab-indicator" style="tab-size:8;">\\u0009</span></hl>complectitur<span class="tab-indicator" style="tab-size:8;">\\u0009</span>verbis, quod vult, et dicit plane, quod intellegam;</div></td>
+            <td class="content both"><div class="contentText">nam et<hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl>complectitur<span class="tab-indicator" style="tab-size:8;">\u0009</span>verbis, quod vult, et dicit plane, quod intellegam;</div></td>
             <td class="right lineNum" data-value="130"></td>
             <td class="content both"><div class="contentText">nam et complectitur verbis, quod vult, et dicit plane, quod intellegam;</div></td>
           </tr>
@@ -120,7 +120,7 @@
             <td class="left lineNum" data-value="165"></td>
             <td class="content both"><div class="contentText"></div></td>
             <td class="right lineNum" data-value="147"></td>
-            <td class="content both"><div class="contentText">in physicis, <hl><span class="tab-indicator" style="tab-size:8;">\\u0009</span></hl> quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
+            <td class="content both"><div class="contentText">in physicis, <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl> quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
           </tr>
         </tbody>
 
@@ -131,25 +131,19 @@
 
 suite('gr-diff-highlight', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate()[1];
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('comment events', () => {
     let builder;
 
     setup(() => {
       builder = {
-        getContentsByLineRange: sandbox.stub().returns([]),
-        getLineElByChild: sandbox.stub().returns({}),
-        getSideByLineEl: sandbox.stub().returns('other-side'),
+        getContentsByLineRange: sinon.stub().returns([]),
+        getLineElByChild: sinon.stub().returns({}),
+        getSideByLineEl: sinon.stub().returns('other-side'),
       };
       element._cachedDiffBuilder = builder;
     });
@@ -162,7 +156,7 @@
       element.appendChild(threadEl);
       element.commentRanges = [{side: 'right'}];
 
-      sandbox.stub(element, 'set');
+      sinon.stub(element, 'set');
       threadEl.dispatchEvent(new CustomEvent(
           'comment-thread-mouseenter', {bubbles: true, composed: true}));
       assert.isFalse(element.set.called);
@@ -187,7 +181,7 @@
         end_character: 6,
       }}];
 
-      sandbox.stub(element, 'set');
+      sinon.stub(element, 'set');
       threadEl.dispatchEvent(new CustomEvent(
           'comment-thread-mouseenter', {bubbles: true, composed: true}));
       assert.isTrue(element.set.called);
@@ -204,7 +198,7 @@
       element.appendChild(threadEl);
       element.commentRanges = [{side: 'right'}];
 
-      sandbox.stub(element, 'set');
+      sinon.stub(element, 'set');
       threadEl.dispatchEvent(new CustomEvent(
           'comment-thread-mouseleave', {bubbles: true, composed: true}));
       assert.isFalse(element.set.called);
@@ -212,7 +206,7 @@
 
     test(`create-range-comment for range when create-comment-requested
           is fired`, () => {
-      sandbox.stub(element, '_removeActionBox');
+      sinon.stub(element, '_removeActionBox');
       element.selectedRange = {
         side: 'left',
         range: {
@@ -273,16 +267,16 @@
     setup(() => {
       contentStubs = [];
       stub('gr-selection-action-box', {
-        placeAbove: sandbox.stub(),
-        placeBelow: sandbox.stub(),
+        placeAbove: sinon.stub(),
+        placeBelow: sinon.stub(),
       });
       diff = element.querySelector('#diffTable');
       builder = {
-        getContentTdByLine: sandbox.stub(),
-        getContentTdByLineEl: sandbox.stub(),
+        getContentTdByLine: sinon.stub(),
+        getContentTdByLineEl: sinon.stub(),
         getLineElByChild,
-        getLineNumberByChild: sandbox.stub(),
-        getSideByLineEl: sandbox.stub(),
+        getLineNumberByChild: sinon.stub(),
+        getSideByLineEl: sinon.stub(),
       };
       element._cachedDiffBuilder = builder;
     });
@@ -294,7 +288,7 @@
 
     test('single first line', () => {
       const content = stubContent(1, 'right');
-      sandbox.spy(element, '_positionActionBox');
+      sinon.spy(element, '_positionActionBox');
       emulateSelection(content.firstChild, 5, content.firstChild, 12);
       const actionBox = element.shadowRoot
           .querySelector('gr-selection-action-box');
@@ -304,7 +298,7 @@
     test('multiline starting on first line', () => {
       const startContent = stubContent(1, 'right');
       const endContent = stubContent(2, 'right');
-      sandbox.spy(element, '_positionActionBox');
+      sinon.spy(element, '_positionActionBox');
       emulateSelection(
           startContent.firstChild, 10, endContent.lastChild, 7);
       const actionBox = element.shadowRoot
@@ -314,7 +308,7 @@
 
     test('single line', () => {
       const content = stubContent(138, 'left');
-      sandbox.spy(element, '_positionActionBox');
+      sinon.spy(element, '_positionActionBox');
       emulateSelection(content.firstChild, 5, content.firstChild, 12);
       const actionBox = element.shadowRoot
           .querySelector('gr-selection-action-box');
@@ -332,7 +326,7 @@
     test('multiline', () => {
       const startContent = stubContent(119, 'right');
       const endContent = stubContent(120, 'right');
-      sandbox.spy(element, '_positionActionBox');
+      sinon.spy(element, '_positionActionBox');
       emulateSelection(
           startContent.firstChild, 10, endContent.lastChild, 7);
       const actionBox = element.shadowRoot
@@ -360,7 +354,7 @@
       endRange.setStart(endContent.lastChild, 6);
       endRange.setEnd(endContent.lastChild, 7);
 
-      const getRangeAtStub = sandbox.stub();
+      const getRangeAtStub = sinon.stub();
       getRangeAtStub
           .onFirstCall().returns(startRange)
           .onSecondCall()
@@ -368,7 +362,7 @@
       const selection = {
         rangeCount: 2,
         getRangeAt: getRangeAtStub,
-        removeAllRanges: sandbox.stub(),
+        removeAllRanges: sinon.stub(),
       };
       element._handleSelection(selection);
       const {range} = element.selectedRange;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
index a0e2bbc..d0e6b62 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
@@ -26,22 +26,17 @@
 
 suite('gr-diff-host tests', () => {
   let element;
-  let sandbox;
+
   let getLoggedIn;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     getLoggedIn = false;
     stub('gr-rest-api-interface', {
       async getLoggedIn() { return getLoggedIn; },
     });
     element = basicFixture.instantiate();
-    sandbox.stub(element.reporting, 'time');
-    sandbox.stub(element.reporting, 'timeEnd');
-  });
-
-  teardown(() => {
-    sandbox.restore();
+    sinon.stub(element.reporting, 'time');
+    sinon.stub(element.reporting, 'timeEnd');
   });
 
   suite('plugin layers', () => {
@@ -61,7 +56,7 @@
 
   suite('handle comment-update', () => {
     setup(() => {
-      sandbox.stub(element, '_commentsChanged');
+      sinon.stub(element, '_commentsChanged');
       element.comments = {
         meta: {
           changeNum: '42',
@@ -107,7 +102,7 @@
         side: 'PARENT',
         __commentSide: 'left',
       };
-      const diffCommentsModifiedStub = sandbox.stub();
+      const diffCommentsModifiedStub = sinon.stub();
       element.addEventListener('diff-comments-modified',
           diffCommentsModifiedStub);
       element.comments.left.push(comment);
@@ -132,7 +127,7 @@
         side: 'PARENT',
         __commentSide: 'left',
       };
-      const diffCommentsModifiedStub = sandbox.stub();
+      const diffCommentsModifiedStub = sinon.stub();
       element.addEventListener('diff-comments-modified',
           diffCommentsModifiedStub);
       element.comments.left.push(comment);
@@ -151,7 +146,7 @@
   });
 
   test('remove comment', () => {
-    sandbox.stub(element, '_commentsChanged');
+    sinon.stub(element, '_commentsChanged');
     element.comments = {
       meta: {
         changeNum: '42',
@@ -305,13 +300,13 @@
     });
 
     test('ends total and syntax timer after syntax layer processing', done => {
-      sandbox.stub(element.reporting, 'diffViewContentDisplayed');
+      sinon.stub(element.reporting, 'diffViewContentDisplayed');
       let notifySyntaxProcessed;
-      sandbox.stub(element.$.syntaxLayer, 'process').returns(new Promise(
+      sinon.stub(element.$.syntaxLayer, 'process').returns(new Promise(
           resolve => {
             notifySyntaxProcessed = resolve;
           }));
-      sandbox.stub(element.$.restAPI, 'getDiff').returns(
+      sinon.stub(element.$.restAPI, 'getDiff').returns(
           Promise.resolve({content: []}));
       element.patchRange = {};
       element.$.restAPI.getDiffPreferences().then(prefs => {
@@ -334,26 +329,30 @@
     });
 
     test('ends total timer w/ no syntax layer processing', done => {
-      sandbox.stub(element.$.restAPI, 'getDiff').returns(
+      sinon.stub(element.$.restAPI, 'getDiff').returns(
           Promise.resolve({content: []}));
       element.patchRange = {};
       element.reload();
       // Multiple cascading microtasks are scheduled.
       setTimeout(() => {
-        assert.isTrue(element.reporting.timeEnd.calledOnce);
-        assert.isTrue(element.reporting.timeEnd.calledWithExactly(
-            'Diff Total Render'));
+        // Reporting can be called with other parameters (ex. PluginsLoaded),
+        // but only 'Diff Total Render' is important in this test.
+        assert.equal(
+            element.reporting.timeEnd.getCalls()
+                .filter(call => call.calledWithExactly('Diff Total Render'))
+                .length,
+            1);
         done();
       });
     });
 
     test('completes reload promise after syntax layer processing', done => {
       let notifySyntaxProcessed;
-      sandbox.stub(element.$.syntaxLayer, 'process').returns(new Promise(
+      sinon.stub(element.$.syntaxLayer, 'process').returns(new Promise(
           resolve => {
             notifySyntaxProcessed = resolve;
           }));
-      sandbox.stub(element.$.restAPI, 'getDiff').returns(
+      sinon.stub(element.$.restAPI, 'getDiff').returns(
           Promise.resolve({content: []}));
       element.patchRange = {};
       let reloadComplete = false;
@@ -379,10 +378,10 @@
   });
 
   test('reload() cancels before network resolves', () => {
-    const cancelStub = sandbox.stub(element.$.diff, 'cancel');
+    const cancelStub = sinon.stub(element.$.diff, 'cancel');
 
     // Stub the network calls into requests that never resolve.
-    sandbox.stub(element, '_getDiff', () => new Promise(() => {}));
+    sinon.stub(element, '_getDiff').callsFake(() => new Promise(() => {}));
     element.patchRange = {};
 
     element.reload();
@@ -396,9 +395,9 @@
     });
 
     test('reload() loads files weblinks', () => {
-      const weblinksStub = sandbox.stub(GerritNav, '_generateWeblinks')
+      const weblinksStub = sinon.stub(GerritNav, '_generateWeblinks')
           .returns({name: 'stubb', url: '#s'});
-      sandbox.stub(element.$.restAPI, 'getDiff').returns(Promise.resolve({
+      sinon.stub(element.$.restAPI, 'getDiff').returns(Promise.resolve({
         content: [],
       }));
       element.projectName = 'test-project';
@@ -431,7 +430,7 @@
     });
 
     test('prefetch getDiff', done => {
-      const diffRestApiStub = sandbox.stub(element.$.restAPI, 'getDiff')
+      const diffRestApiStub = sinon.stub(element.$.restAPI, 'getDiff')
           .returns(Promise.resolve({content: []}));
       element.changeNum = 123;
       element.patchRange = {basePatchNum: 1, patchNum: 2};
@@ -454,9 +453,9 @@
     });
 
     test('reload resolves on error', () => {
-      const onErrStub = sandbox.stub(element, '_handleGetDiffError');
+      const onErrStub = sinon.stub(element, '_handleGetDiffError');
       const error = {ok: false, status: 500};
-      sandbox.stub(element.$.restAPI, 'getDiff',
+      sinon.stub(element.$.restAPI, 'getDiff').callsFake(
           (changeNum, basePatchNum, patchNum, path, onErr) => {
             onErr(error);
           });
@@ -515,12 +514,12 @@
           'wsAAAAAAAAAAAAA/////w==',
           type: 'image/bmp',
         };
-        sandbox.stub(element.$.restAPI,
-            'getB64FileContents',
-            (changeId, patchNum, path, opt_parentIndex) => Promise.resolve(
-                opt_parentIndex === 1 ? mockFile1 :
-                  mockFile2)
-        );
+        sinon.stub(element.$.restAPI,
+            'getB64FileContents')
+            .callsFake(
+                (changeId, patchNum, path, opt_parentIndex) => Promise.resolve(
+                    opt_parentIndex === 1 ? mockFile1 : mockFile2)
+            );
 
         element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
         element.comments = {
@@ -547,7 +546,7 @@
           content: [{skip: 66}],
           binary: true,
         };
-        sandbox.stub(element.$.restAPI, 'getDiff')
+        sinon.stub(element.$.restAPI, 'getDiff')
             .returns(Promise.resolve(mockDiff));
 
         const rendered = () => {
@@ -628,7 +627,7 @@
           content: [{skip: 66}],
           binary: true,
         };
-        sandbox.stub(element.$.restAPI, 'getDiff')
+        sinon.stub(element.$.restAPI, 'getDiff')
             .returns(Promise.resolve(mockDiff));
 
         const rendered = () => {
@@ -710,7 +709,7 @@
           content: [{skip: 66}],
           binary: true,
         };
-        sandbox.stub(element.$.restAPI, 'getDiff')
+        sinon.stub(element.$.restAPI, 'getDiff')
             .returns(Promise.resolve(mockDiff));
 
         element.addEventListener('render', () => {
@@ -751,7 +750,7 @@
           content: [{skip: 66}],
           binary: true,
         };
-        sandbox.stub(element.$.restAPI, 'getDiff')
+        sinon.stub(element.$.restAPI, 'getDiff')
             .returns(Promise.resolve(mockDiff));
 
         element.addEventListener('render', () => {
@@ -794,7 +793,7 @@
         };
         mockFile1.type = 'image/jpeg-evil';
 
-        sandbox.stub(element.$.restAPI, 'getDiff')
+        sinon.stub(element.$.restAPI, 'getDiff')
             .returns(Promise.resolve(mockDiff));
 
         element.addEventListener('render', () => {
@@ -817,7 +816,7 @@
   });
 
   test('delegates cancel()', () => {
-    const stub = sandbox.stub(element.$.diff, 'cancel');
+    const stub = sinon.stub(element.$.diff, 'cancel');
     element.patchRange = {};
     element.reload();
     assert.isTrue(stub.calledOnce);
@@ -826,7 +825,7 @@
 
   test('delegates getCursorStops()', () => {
     const returnValue = [document.createElement('b')];
-    const stub = sandbox.stub(element.$.diff, 'getCursorStops')
+    const stub = sinon.stub(element.$.diff, 'getCursorStops')
         .returns(returnValue);
     assert.equal(element.getCursorStops(), returnValue);
     assert.isTrue(stub.calledOnce);
@@ -835,7 +834,7 @@
 
   test('delegates isRangeSelected()', () => {
     const returnValue = true;
-    const stub = sandbox.stub(element.$.diff, 'isRangeSelected')
+    const stub = sinon.stub(element.$.diff, 'isRangeSelected')
         .returns(returnValue);
     assert.equal(element.isRangeSelected(), returnValue);
     assert.isTrue(stub.calledOnce);
@@ -843,7 +842,7 @@
   });
 
   test('delegates toggleLeftDiff()', () => {
-    const stub = sandbox.stub(element.$.diff, 'toggleLeftDiff');
+    const stub = sinon.stub(element.$.diff, 'toggleLeftDiff');
     element.toggleLeftDiff();
     assert.isTrue(stub.calledOnce);
     assert.equal(stub.lastCall.args.length, 0);
@@ -856,7 +855,7 @@
 
     test('clearBlame', () => {
       element._blame = [];
-      const setBlameSpy = sandbox.spy(element.$.diff.$.diffBuilder, 'setBlame');
+      const setBlameSpy = sinon.spy(element.$.diff.$.diffBuilder, 'setBlame');
       element.clearBlame();
       assert.isNull(element._blame);
       assert.isTrue(setBlameSpy.calledWithExactly(null));
@@ -867,7 +866,7 @@
       const mockBlame = [{id: 'commit id', ranges: [{start: 1, end: 2}]}];
       const showAlertStub = sinon.stub();
       element.addEventListener('show-alert', showAlertStub);
-      const getBlameStub = sandbox.stub(element.$.restAPI, 'getBlame')
+      const getBlameStub = sinon.stub(element.$.restAPI, 'getBlame')
           .returns(Promise.resolve(mockBlame));
       element.changeNum = 42;
       element.patchRange = {patchNum: 5, basePatchNum: 4};
@@ -885,7 +884,7 @@
       const mockBlame = [];
       const showAlertStub = sinon.stub();
       element.addEventListener('show-alert', showAlertStub);
-      sandbox.stub(element.$.restAPI, 'getBlame')
+      sinon.stub(element.$.restAPI, 'getBlame')
           .returns(Promise.resolve(mockBlame));
       element.changeNum = 42;
       element.patchRange = {patchNum: 5, basePatchNum: 4};
@@ -911,7 +910,7 @@
 
   test('delegates addDraftAtLine(el)', () => {
     const param0 = document.createElement('b');
-    const stub = sandbox.stub(element.$.diff, 'addDraftAtLine');
+    const stub = sinon.stub(element.$.diff, 'addDraftAtLine');
     element.addDraftAtLine(param0);
     assert.isTrue(stub.calledOnce);
     assert.equal(stub.lastCall.args.length, 1);
@@ -919,14 +918,14 @@
   });
 
   test('delegates clearDiffContent()', () => {
-    const stub = sandbox.stub(element.$.diff, 'clearDiffContent');
+    const stub = sinon.stub(element.$.diff, 'clearDiffContent');
     element.clearDiffContent();
     assert.isTrue(stub.calledOnce);
     assert.equal(stub.lastCall.args.length, 0);
   });
 
   test('delegates expandAllContext()', () => {
-    const stub = sandbox.stub(element.$.diff, 'expandAllContext');
+    const stub = sinon.stub(element.$.diff, 'expandAllContext');
     element.expandAllContext();
     assert.isTrue(stub.calledOnce);
     assert.equal(stub.lastCall.args.length, 0);
@@ -1024,7 +1023,7 @@
       element = basicFixture.instantiate();
       element.path = 'file.txt';
       element.patchRange = {basePatchNum: 1};
-      reportStub = sandbox.stub(element.reporting, 'reportInteraction');
+      reportStub = sinon.stub(element.reporting, 'reportInteraction');
     });
 
     test('null and content-less', () => {
@@ -1474,9 +1473,9 @@
     });
 
     test('starts syntax layer processing on render event', done => {
-      sandbox.stub(element.$.syntaxLayer, 'process')
+      sinon.stub(element.$.syntaxLayer, 'process')
           .returns(Promise.resolve());
-      sandbox.stub(element.$.restAPI, 'getDiff').returns(
+      sinon.stub(element.$.restAPI, 'getDiff').returns(
           Promise.resolve({content: []}));
       element.reload();
       setTimeout(() => {
@@ -1650,7 +1649,7 @@
     suite('_hasTrailingNewlines', () => {
       test('shared no trailing', () => {
         const diff = undefined;
-        sandbox.stub(element, '_lastChunkForSide')
+        sinon.stub(element, '_lastChunkForSide')
             .returns({ab: ['foo', 'bar']});
         assert.isFalse(element._hasTrailingNewlines(diff, false));
         assert.isFalse(element._hasTrailingNewlines(diff, true));
@@ -1658,7 +1657,7 @@
 
       test('delta trailing in right', () => {
         const diff = undefined;
-        sandbox.stub(element, '_lastChunkForSide')
+        sinon.stub(element, '_lastChunkForSide')
             .returns({a: ['foo', 'bar'], b: ['baz', '']});
         assert.isTrue(element._hasTrailingNewlines(diff, false));
         assert.isFalse(element._hasTrailingNewlines(diff, true));
@@ -1666,7 +1665,7 @@
 
       test('addition', () => {
         const diff = undefined;
-        sandbox.stub(element, '_lastChunkForSide', (diff, leftSide) => {
+        sinon.stub(element, '_lastChunkForSide').callsFake((diff, leftSide) => {
           if (leftSide) { return null; }
           return {b: ['foo', '']};
         });
@@ -1676,7 +1675,7 @@
 
       test('deletion', () => {
         const diff = undefined;
-        sandbox.stub(element, '_lastChunkForSide', (diff, leftSide) => {
+        sinon.stub(element, '_lastChunkForSide').callsFake((diff, leftSide) => {
           if (!leftSide) { return null; }
           return {a: ['foo']};
         });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.js b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.js
index e5ecde0..e84ef2b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.js
@@ -22,17 +22,11 @@
 
 suite('gr-diff-mode-selector tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_computeSelectedClass', () => {
     assert.equal(
         element._computeSelectedClass('SIDE_BY_SIDE', 'SIDE_BY_SIDE'),
@@ -42,7 +36,7 @@
   });
 
   test('setMode', () => {
-    const saveStub = sandbox.stub(element.$.restAPI, 'savePreferences');
+    const saveStub = sinon.stub(element.$.restAPI, 'savePreferences');
 
     // Setting the mode initially does not save prefs.
     element.saveOnChange = true;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js
index ea7f049..a8fba5d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js
@@ -32,14 +32,9 @@
       'fugit assum per.';
 
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
 
-  teardown(() => {
-    sandbox.restore();
   });
 
   suite('not logged in', () => {
@@ -599,7 +594,7 @@
         ],
       };
       const content = _.times(200, _.constant(contentRow));
-      sandbox.stub(element, 'async');
+      sinon.stub(element, 'async');
       element._isScrolling = true;
       element.process(content);
       // Just the files group - no more processing during scrolling.
@@ -619,7 +614,7 @@
         ],
       };
       const content = _.times(200, _.constant(contentRow));
-      sandbox.stub(element, 'async');
+      sinon.stub(element, 'async');
       element.process(content, true);
       assert.equal(element.groups.length, 1);
 
@@ -855,7 +850,7 @@
 
     suite('_breakdown*', () => {
       test('_breakdownChunk breaks down additions', () => {
-        sandbox.spy(element, '_breakdown');
+        sinon.spy(element, '_breakdown');
         const chunk = {b: ['blah', 'blah', 'blah']};
         const result = element._breakdownChunk(chunk);
         assert.deepEqual(result, [chunk]);
@@ -864,7 +859,7 @@
 
       test('_breakdownChunk keeps due_to_rebase for broken down additions',
           () => {
-            sandbox.spy(element, '_breakdown');
+            sinon.spy(element, '_breakdown');
             const chunk = {b: ['blah', 'blah', 'blah'], due_to_rebase: true};
             const result = element._breakdownChunk(chunk);
             for (const subResult of result) {
@@ -912,7 +907,7 @@
 
   test('detaching cancels', () => {
     element = basicFixture.instantiate();
-    sandbox.stub(element, 'cancel');
+    sinon.stub(element, 'cancel');
     element.detached();
     assert(element.cancel.called);
   });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js
index 938833f..c37ac93 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js
@@ -100,14 +100,13 @@
 
 suite('gr-diff-selection', () => {
   let element;
-  let sandbox;
 
   const emulateCopyOn = function(target) {
     const fakeEvent = {
       target,
-      preventDefault: sandbox.stub(),
+      preventDefault: sinon.stub(),
       clipboardData: {
-        setData: sandbox.stub(),
+        setData: sinon.stub(),
       },
     };
     element._getCopyEventTarget.returns(target);
@@ -117,11 +116,11 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(element, '_getCopyEventTarget');
+
+    sinon.stub(element, '_getCopyEventTarget');
     element._cachedDiffBuilder = {
-      getLineElByChild: sandbox.stub().returns({}),
-      getSideByLineEl: sandbox.stub(),
+      getLineElByChild: sinon.stub().returns({}),
+      getSideByLineEl: sinon.stub(),
       diffElement: element.querySelector('#diffTable'),
     };
     element.diff = {
@@ -142,10 +141,6 @@
     };
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('applies selected-left on left side click', () => {
     element.classList.add('selected-right');
     element._cachedDiffBuilder.getSideByLineEl.returns('left');
@@ -170,7 +165,7 @@
   test('applies selected-blame on blame click', () => {
     element.classList.add('selected-left');
     element.diffBuilder.getLineElByChild.returns(null);
-    sandbox.stub(element, '_elementDescendedFromClass',
+    sinon.stub(element, '_elementDescendedFromClass').callsFake(
         (el, className) => className === 'blame');
     MockInteractions.down(element);
     assert.isTrue(
@@ -180,26 +175,26 @@
   });
 
   test('ignores copy for non-content Element', () => {
-    sandbox.stub(element, '_getSelectedText');
+    sinon.stub(element, '_getSelectedText');
     emulateCopyOn(element.querySelector('.not-diff-row'));
     assert.isFalse(element._getSelectedText.called);
   });
 
   test('asks for text for left side Elements', () => {
     element._cachedDiffBuilder.getSideByLineEl.returns('left');
-    sandbox.stub(element, '_getSelectedText');
+    sinon.stub(element, '_getSelectedText');
     emulateCopyOn(element.querySelector('div.contentText'));
     assert.deepEqual(['left', false], element._getSelectedText.lastCall.args);
   });
 
   test('reacts to copy for content Elements', () => {
-    sandbox.stub(element, '_getSelectedText');
+    sinon.stub(element, '_getSelectedText');
     emulateCopyOn(element.querySelector('div.contentText'));
     assert.isTrue(element._getSelectedText.called);
   });
 
   test('copy event is prevented for content Elements', () => {
-    sandbox.stub(element, '_getSelectedText');
+    sinon.stub(element, '_getSelectedText');
     element._cachedDiffBuilder.getSideByLineEl.returns('left');
     element._getSelectedText.returns('test');
     const event = emulateCopyOn(element.querySelector('div.contentText'));
@@ -207,7 +202,7 @@
   });
 
   test('inserts text into clipboard on copy', () => {
-    sandbox.stub(element, '_getSelectedText').returns('the text');
+    sinon.stub(element, '_getSelectedText').returns('the text');
     const event = emulateCopyOn(element.querySelector('div.contentText'));
     assert.deepEqual(
         ['Text', 'the text'], event.clipboardData.setData.lastCall.args);
@@ -230,10 +225,11 @@
 
   test('_setClasses removes before it ads', () => {
     element.classList.add('selected-right');
-    const addStub = sandbox.stub(element.classList, 'add');
-    const removeStub = sandbox.stub(element.classList, 'remove', () => {
-      assert.isFalse(addStub.called);
-    });
+    const addStub = sinon.stub(element.classList, 'add');
+    const removeStub = sinon.stub(element.classList, 'remove').callsFake(
+        () => {
+          assert.isFalse(addStub.called);
+        });
     element._setClasses(['selected-comment', 'selected-left']);
     assert.isTrue(addStub.called);
     assert.isTrue(removeStub.called);
@@ -295,7 +291,7 @@
   test('defers to default behavior for textarea', () => {
     element.classList.add('selected-left');
     element.classList.remove('selected-right');
-    const selectedTextSpy = sandbox.spy(element, '_getSelectedText');
+    const selectedTextSpy = sinon.spy(element, '_getSelectedText');
     emulateCopyOn(element.querySelector('textarea'));
     assert.isFalse(selectedTextSpy.called);
   });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
index d619536..73e6f0c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
@@ -29,7 +29,6 @@
 suite('gr-diff-view tests', () => {
   suite('basic tests', () => {
     let element;
-    let sandbox;
 
     suiteSetup(() => {
       const kb = TestKeyboardShortcutBinder.push();
@@ -79,8 +78,6 @@
     }
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
-
       stub('gr-rest-api-interface', {
         getConfig() {
           return Promise.resolve({change: {}});
@@ -117,14 +114,10 @@
       return element._loadComments();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('params change triggers diffViewDisplayed()', () => {
-      sandbox.stub(element.reporting, 'diffViewDisplayed');
-      sandbox.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
-      sandbox.spy(element, '_paramsChanged');
+      sinon.stub(element.reporting, 'diffViewDisplayed');
+      sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
+      sinon.spy(element, '_paramsChanged');
       element.params = {
         view: GerritNav.View.DIFF,
         changeNum: '42',
@@ -141,10 +134,10 @@
     test('params change cases blame to load if it was set to true', () => {
       // Blame loads for subsequent files if it was loaded for one file
       element._isBlameLoaded = true;
-      sandbox.stub(element.reporting, 'diffViewDisplayed');
-      sandbox.stub(element, '_loadBlame');
-      sandbox.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
-      sandbox.spy(element, '_paramsChanged');
+      sinon.stub(element.reporting, 'diffViewDisplayed');
+      sinon.stub(element, '_loadBlame');
+      sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
+      sinon.spy(element, '_paramsChanged');
       element.params = {
         view: GerritNav.View.DIFF,
         changeNum: '42',
@@ -160,7 +153,7 @@
     });
 
     test('toggle left diff with a hotkey', () => {
-      const toggleLeftDiffStub = sandbox.stub(
+      const toggleLeftDiffStub = sinon.stub(
           element.$.diffHost, 'toggleLeftDiff');
       MockInteractions.pressAndReleaseKeyOn(element, 65, 'shift', 'a');
       assert.isTrue(toggleLeftDiffStub.calledOnce);
@@ -184,8 +177,8 @@
       element.changeViewState.selectedFileIndex = 1;
       element._loggedIn = true;
 
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
-      const changeNavStub = sandbox.stub(GerritNav, 'navigateToChange');
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
+      const changeNavStub = sinon.stub(GerritNav, 'navigateToChange');
 
       MockInteractions.pressAndReleaseKeyOn(element, 85, null, 'u');
       assert(changeNavStub.lastCall.calledWith(element._change),
@@ -219,7 +212,7 @@
       assert.isTrue(element._loading);
 
       const showPrefsStub =
-          sandbox.stub(element.$.diffPreferencesDialog, 'open',
+          sinon.stub(element.$.diffPreferencesDialog, 'open').callsFake(
               () => Promise.resolve());
 
       MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
@@ -229,24 +222,24 @@
       MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
       assert(showPrefsStub.calledOnce);
 
-      let scrollStub = sandbox.stub(element.$.cursor, 'moveToNextChunk');
+      let scrollStub = sinon.stub(element.$.cursor, 'moveToNextChunk');
       MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
       assert(scrollStub.calledOnce);
 
-      scrollStub = sandbox.stub(element.$.cursor, 'moveToPreviousChunk');
+      scrollStub = sinon.stub(element.$.cursor, 'moveToPreviousChunk');
       MockInteractions.pressAndReleaseKeyOn(element, 80, null, 'p');
       assert(scrollStub.calledOnce);
 
-      scrollStub = sandbox.stub(element.$.cursor, 'moveToNextCommentThread');
+      scrollStub = sinon.stub(element.$.cursor, 'moveToNextCommentThread');
       MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
       assert(scrollStub.calledOnce);
 
-      scrollStub = sandbox.stub(element.$.cursor,
+      scrollStub = sinon.stub(element.$.cursor,
           'moveToPreviousCommentThread');
       MockInteractions.pressAndReleaseKeyOn(element, 80, 'shift', 'p');
       assert(scrollStub.calledOnce);
 
-      const computeContainerClassStub = sandbox.stub(element.$.diffHost.$.diff,
+      const computeContainerClassStub = sinon.stub(element.$.diffHost.$.diff,
           '_computeContainerClass');
       MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
       assert(computeContainerClassStub.lastCall.calledWithExactly(
@@ -256,8 +249,8 @@
       assert(computeContainerClassStub.lastCall.calledWithExactly(
           false, 'SIDE_BY_SIDE', false));
 
-      sandbox.stub(element, '_setReviewed');
-      sandbox.spy(element, '_handleToggleFileReviewed');
+      sinon.stub(element, '_setReviewed');
+      sinon.spy(element, '_handleToggleFileReviewed');
       element.$.reviewed.checked = false;
       MockInteractions.pressAndReleaseKeyOn(element, 82, 'shift', 'r');
       assert.isFalse(element._setReviewed.called);
@@ -270,7 +263,7 @@
     });
 
     test('shift+x shortcut expands all diff context', () => {
-      const expandStub = sandbox.stub(element.$.diffHost, 'expandAllContext');
+      const expandStub = sinon.stub(element.$.diffHost, 'expandAllContext');
       MockInteractions.pressAndReleaseKeyOn(element, 88, 'shift', 'x');
       flushAsynchronousOperations();
       assert.isTrue(expandStub.called);
@@ -281,8 +274,8 @@
         basePatchNum: '5',
         patchNum: '10',
       };
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
       element._handleDiffAgainstBase(new CustomEvent(''));
       const args = diffNavStub.getCall(0).args;
       assert.equal(args[2], 10);
@@ -294,9 +287,9 @@
         basePatchNum: '5',
         patchNum: '10',
       };
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
-      sandbox.stub(element, 'computeLatestPatchNum').returns(12);
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      sinon.stub(element, 'computeLatestPatchNum').returns(12);
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
       element._handleDiffAgainstLatest(new CustomEvent(''));
       const args = diffNavStub.getCall(0).args;
       assert.equal(args[2], 12);
@@ -309,9 +302,9 @@
         patchNum: 3,
         basePatchNum: 1,
       };
-      sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      sinon.stub(element, 'computeLatestPatchNum').returns(10);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
       element._handleDiffBaseAgainstLeft(new CustomEvent(''));
       assert(diffNavStub.called);
       const args = diffNavStub.getCall(0).args;
@@ -325,9 +318,9 @@
         basePatchNum: 1,
         patchNum: 3,
       };
-      sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      sinon.stub(element, 'computeLatestPatchNum').returns(10);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
       element._handleDiffRightAgainstLatest(new CustomEvent(''));
       assert(diffNavStub.called);
       const args = diffNavStub.getCall(0).args;
@@ -341,9 +334,9 @@
         basePatchNum: 1,
         patchNum: 3,
       };
-      sandbox.stub(element, 'computeLatestPatchNum').returns(10);
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      sinon.stub(element, 'computeLatestPatchNum').returns(10);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
       element._handleDiffBaseAgainstLatest(new CustomEvent(''));
       assert(diffNavStub.called);
       const args = diffNavStub.getCall(0).args;
@@ -368,8 +361,8 @@
           ['chell.go', 'glados.txt', 'wheatley.md']);
       element._path = 'glados.txt';
 
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
-      const changeNavStub = sandbox.stub(GerritNav, 'navigateToChange');
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
+      const changeNavStub = sinon.stub(GerritNav, 'navigateToChange');
 
       MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a');
       assert.isTrue(changeNavStub.notCalled, 'The `a` keyboard shortcut ' +
@@ -436,8 +429,8 @@
           ['chell.go', 'glados.txt', 'wheatley.md']);
       element._path = 'glados.txt';
 
-      const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
-      const changeNavStub = sandbox.stub(GerritNav, 'navigateToChange');
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
+      const changeNavStub = sinon.stub(GerritNav, 'navigateToChange');
 
       MockInteractions.pressAndReleaseKeyOn(element, 65, null, 'a');
       assert.isTrue(changeNavStub.notCalled, 'The `a` keyboard shortcut ' +
@@ -497,7 +490,7 @@
           b: {_number: 2, commit: {parents: []}},
         },
       };
-      const redirectStub = sandbox.stub(GerritNav, 'navigateToRelativeUrl');
+      const redirectStub = sinon.stub(GerritNav, 'navigateToRelativeUrl');
       flush(() => {
         const editBtn = element.shadowRoot
             .querySelector('.editButton gr-button');
@@ -607,8 +600,8 @@
     });
 
     test('prefsButton opens gr-diff-preferences', () => {
-      const handlePrefsTapSpy = sandbox.spy(element, '_handlePrefsTap');
-      const overlayOpenStub = sandbox.stub(element.$.diffPreferencesDialog,
+      const handlePrefsTapSpy = sinon.spy(element, '_handlePrefsTap');
+      const overlayOpenStub = sinon.stub(element.$.diffPreferencesDialog,
           'open');
       const prefsButton =
           dom(element.root).querySelector('.prefsButton');
@@ -623,9 +616,9 @@
       const path = '/test';
       element.$.commentAPI.loadAll().then(comments => {
         const commentCountStub =
-            sandbox.stub(comments, 'computeCommentCount');
+            sinon.stub(comments, 'computeCommentCount');
         const unresolvedCountStub =
-            sandbox.stub(comments, 'computeUnresolvedNum');
+            sinon.stub(comments, 'computeUnresolvedNum');
         commentCountStub.withArgs({patchNum: 1, path}).returns(0);
         commentCountStub.withArgs({patchNum: 2, path}).returns(1);
         commentCountStub.withArgs({patchNum: 3, path}).returns(2);
@@ -659,14 +652,14 @@
 
     suite('url params', () => {
       setup(() => {
-        sandbox.stub(
+        sinon.stub(
             GerritNav,
-            'getUrlForDiff',
-            (c, p, pn, bpn) => `${c._number}-${p}-${pn}-${bpn}`);
-        sandbox.stub(
+            'getUrlForDiff')
+            .callsFake((c, p, pn, bpn) => `${c._number}-${p}-${pn}-${bpn}`);
+        sinon.stub(
             GerritNav
-            , 'getUrlForChange',
-            (c, pn, bpn) => `${c._number}-${pn}-${bpn}`);
+            , 'getUrlForChange')
+            .callsFake((c, pn, bpn) => `${c._number}-${pn}-${bpn}`);
       });
 
       test('_formattedFiles', () => {
@@ -793,7 +786,7 @@
     });
 
     test('_handlePatchChange calls navigateToDiff correctly', () => {
-      const navigateStub = sandbox.stub(GerritNav, 'navigateToDiff');
+      const navigateStub = sinon.stub(GerritNav, 'navigateToDiff');
       element._change = {_number: 321, project: 'foo/bar'};
       element._path = 'path/to/file.txt';
 
@@ -815,12 +808,12 @@
     });
 
     test('_prefs.manual_review is respected', () => {
-      const saveReviewedStub = sandbox.stub(element, '_saveReviewedState',
-          () => Promise.resolve());
-      const getReviewedStub = sandbox.stub(element, '_getReviewedStatus',
-          () => Promise.resolve());
+      const saveReviewedStub = sinon.stub(element, '_saveReviewedState')
+          .callsFake(() => Promise.resolve());
+      const getReviewedStub = sinon.stub(element, '_getReviewedStatus')
+          .callsFake(() => Promise.resolve());
 
-      sandbox.stub(element.$.diffHost, 'reload');
+      sinon.stub(element.$.diffHost, 'reload');
       element._loggedIn = true;
       element.params = {
         view: GerritNav.View.DIFF,
@@ -843,9 +836,9 @@
     });
 
     test('file review status', () => {
-      const saveReviewedStub = sandbox.stub(element, '_saveReviewedState',
-          () => Promise.resolve());
-      sandbox.stub(element.$.diffHost, 'reload');
+      const saveReviewedStub = sinon.stub(element, '_saveReviewedState')
+          .callsFake(() => Promise.resolve());
+      sinon.stub(element.$.diffHost, 'reload');
 
       element._loggedIn = true;
       element.params = {
@@ -880,7 +873,7 @@
     });
 
     test('file review status with edit loaded', () => {
-      const saveReviewedStub = sandbox.stub(element, '_saveReviewedState');
+      const saveReviewedStub = sinon.stub(element, '_saveReviewedState');
 
       element._patchRange = {patchNum: element.EDIT_NAME};
       flushAsynchronousOperations();
@@ -891,8 +884,8 @@
     });
 
     test('hash is determined from params', done => {
-      sandbox.stub(element.$.diffHost, 'reload');
-      sandbox.stub(element, '_initCursor');
+      sinon.stub(element.$.diffHost, 'reload');
+      sinon.stub(element, '_initCursor');
 
       element._loggedIn = true;
       element.params = {
@@ -939,7 +932,8 @@
       const prefsPromise = new Promise(resolve => {
         resolvePrefs = resolve;
       });
-      sandbox.stub(element.$.restAPI, 'getPreferences', () => prefsPromise);
+      sinon.stub(element.$.restAPI, 'getPreferences')
+          .callsFake(() => prefsPromise);
 
       // Attach a new gr-diff-view so we can intercept the preferences fetch.
       const view = document.createElement('gr-diff-view');
@@ -968,9 +962,9 @@
 
     suite('_commitRange', () => {
       setup(() => {
-        sandbox.stub(element.$.diffHost, 'reload');
-        sandbox.stub(element, '_initCursor');
-        sandbox.stub(element, '_getChangeDetail').returns(Promise.resolve({
+        sinon.stub(element.$.diffHost, 'reload');
+        sinon.stub(element, '_initCursor');
+        sinon.stub(element, '_getChangeDetail').returns(Promise.resolve({
           _number: 42,
           revisions: {
             'commit-sha-1': {
@@ -1066,9 +1060,9 @@
     });
 
     test('_onLineSelected', () => {
-      const getUrlStub = sandbox.stub(GerritNav, 'getUrlForDiffById');
-      const replaceStateStub = sandbox.stub(history, 'replaceState');
-      sandbox.stub(element.$.cursor, 'getAddress')
+      const getUrlStub = sinon.stub(GerritNav, 'getUrlForDiffById');
+      const replaceStateStub = sinon.stub(history, 'replaceState');
+      sinon.stub(element.$.cursor, 'getAddress')
           .returns({number: 123, isLeftSide: false});
 
       element._changeNum = 321;
@@ -1087,10 +1081,10 @@
     });
 
     test('_onLineSelected w/o line address', () => {
-      const getUrlStub = sandbox.stub(GerritNav, 'getUrlForDiffById');
-      sandbox.stub(history, 'replaceState');
-      sandbox.stub(element.$.cursor, 'moveToLineNumber');
-      sandbox.stub(element.$.cursor, 'getAddress').returns(null);
+      const getUrlStub = sinon.stub(GerritNav, 'getUrlForDiffById');
+      sinon.stub(history, 'replaceState');
+      sinon.stub(element.$.cursor, 'moveToLineNumber');
+      sinon.stub(element.$.cursor, 'getAddress').returns(null);
       element._changeNum = 321;
       element._change = {_number: 321, project: 'foo/bar'};
       element._patchRange = {basePatchNum: '3', patchNum: '5'};
@@ -1114,7 +1108,7 @@
     });
 
     test('_handleToggleDiffMode', () => {
-      sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+      sinon.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
       const e = {preventDefault: () => {}};
       // Initial state.
       assert.equal(element._getDiffViewMode(), 'SIDE_BY_SIDE');
@@ -1135,11 +1129,11 @@
       });
 
       test('has paths', done => {
-        sandbox.stub(element, '_getPaths').returns({
+        sinon.stub(element, '_getPaths').returns({
           'path/to/file/one.cpp': [{patch_set: 3, message: 'lorem'}],
           'path-to/file/two.py': [{patch_set: 5, message: 'ipsum'}],
         });
-        sandbox.stub(element, '_getCommentsForPath').returns({meta: {}});
+        sinon.stub(element, '_getCommentsForPath').returns({meta: {}});
         element._changeNum = '42';
         element._patchRange = {
           basePatchNum: '3',
@@ -1202,8 +1196,8 @@
         let navToDiffStub;
 
         setup(() => {
-          navToChangeStub = sandbox.stub(element, '_navToChangeView');
-          navToDiffStub = sandbox.stub(GerritNav, 'navigateToDiff');
+          navToChangeStub = sinon.stub(element, '_navToChangeView');
+          navToDiffStub = sinon.stub(GerritNav, 'navigateToDiff');
           element._files = getFilesFromFileList([
             'path/one.jpg', 'path/two.m4v', 'path/three.wav',
           ]);
@@ -1305,7 +1299,7 @@
       const promises = [];
       element.$.restAPI.getReviewedFiles.restore();
 
-      sandbox.stub(element.$.restAPI, 'getReviewedFiles')
+      sinon.stub(element.$.restAPI, 'getReviewedFiles')
           .returns(Promise.resolve(['path']));
 
       promises.push(element._getReviewedStatus(true, null, null, 'path')
@@ -1322,14 +1316,15 @@
 
     suite('blame', () => {
       test('toggle blame with button', () => {
-        const toggleBlame = sandbox.stub(
-            element.$.diffHost, 'loadBlame', () => Promise.resolve());
+        const toggleBlame = sinon.stub(
+            element.$.diffHost, 'loadBlame')
+            .callsFake(() => Promise.resolve());
         MockInteractions.tap(element.$.toggleBlame);
         assert.isTrue(toggleBlame.calledOnce);
       });
       test('toggle blame with shortcut', () => {
-        const toggleBlame = sandbox.stub(
-            element.$.diffHost, 'loadBlame', () => Promise.resolve());
+        const toggleBlame = sinon.stub(
+            element.$.diffHost, 'loadBlame').callsFake(() => Promise.resolve());
         MockInteractions.pressAndReleaseKeyOn(element, 66, null, 'b');
         assert.isTrue(toggleBlame.calledOnce);
       });
@@ -1346,7 +1341,7 @@
       };
 
       test('reviewed checkbox', () => {
-        sandbox.stub(element, '_handlePatchChange');
+        sinon.stub(element, '_handlePatchChange');
         element._patchRange = {patchNum: '1'};
         // Reviewed checkbox should be shown.
         assert.isTrue(isVisible(element.$.reviewed));
@@ -1358,9 +1353,9 @@
     });
 
     test('_paramsChanged sets in projectLookup', () => {
-      sandbox.stub(element, '_getLineOfInterest');
-      sandbox.stub(element, '_initCursor');
-      const setStub = sandbox.stub(element.$.restAPI, 'setInProjectLookup');
+      sinon.stub(element, '_getLineOfInterest');
+      sinon.stub(element, '_initCursor');
+      const setStub = sinon.stub(element.$.restAPI, 'setInProjectLookup');
       element._paramsChanged({
         view: GerritNav.View.DIFF,
         changeNum: 101,
@@ -1375,8 +1370,8 @@
       element._files = getFilesFromFileList(['file1', 'file2', 'file3']);
       element._reviewedFiles = new Set(['file1', 'file2']);
       element._path = 'file1';
-      const reviewedStub = sandbox.stub(element, '_setReviewed');
-      const navStub = sandbox.stub(element, '_navToFile');
+      const reviewedStub = sinon.stub(element, '_setReviewed');
+      const navStub = sinon.stub(element, '_navToFile');
       MockInteractions.pressAndReleaseKeyOn(element, 77, 'shift', 'm');
       flushAsynchronousOperations();
 
@@ -1390,9 +1385,9 @@
 
     test('File change should trigger navigateToDiff once', () => {
       element._files = getFilesFromFileList(['file1', 'file2', 'file3']);
-      sandbox.stub(element, '_getLineOfInterest');
-      sandbox.stub(element, '_initCursor');
-      sandbox.stub(GerritNav, 'navigateToDiff');
+      sinon.stub(element, '_getLineOfInterest');
+      sinon.stub(element, '_initCursor');
+      sinon.stub(GerritNav, 'navigateToDiff');
 
       // Load file1
       element._paramsChanged({
@@ -1518,10 +1513,8 @@
   });
 
   suite('gr-diff-view tests unmodified files with comments', () => {
-    let sandbox;
     let element;
     setup(() => {
-      sandbox = sinon.sandbox.create();
       const changedFiles = {
         'file1.txt': {},
         'a/b/test.c': {},
@@ -1542,10 +1535,6 @@
       return element._loadComments();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('_getFiles add files with comments without changes', () => {
       const patchChangeRecord = {
         base: {
@@ -1554,7 +1543,7 @@
         },
       };
       const changeComments = {
-        getPaths: sandbox.stub().returns({
+        getPaths: sinon.stub().returns({
           'file2.txt': {},
           'file1.txt': {},
         }),
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
index a10db97..d60d174 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
@@ -36,16 +36,11 @@
 
 suite('gr-diff tests', () => {
   let element;
-  let sandbox;
 
   const MINIMAL_PREFS = {tab_size: 2, line_length: 80};
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
 
-  teardown(() => {
-    sandbox.restore();
   });
 
   suite('selectionchange event handling', () => {
@@ -55,7 +50,7 @@
 
     setup(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element.$.highlights, 'handleSelectionChange');
+      sinon.stub(element.$.highlights, 'handleSelectionChange');
     });
 
     test('enabled if logged in', () => {
@@ -73,7 +68,7 @@
 
   test('cancel', () => {
     element = basicFixture.instantiate();
-    const cancelStub = sandbox.stub(element.$.diffBuilder, 'cancel');
+    const cancelStub = sinon.stub(element.$.diffBuilder, 'cancel');
     element.cancel();
     assert.isTrue(cancelStub.calledOnce);
   });
@@ -195,8 +190,8 @@
     });
 
     test('addDraftAtLine', () => {
-      sandbox.stub(element, '_selectLine');
-      const loggedInErrorSpy = sandbox.spy();
+      sinon.stub(element, '_selectLine');
+      const loggedInErrorSpy = sinon.spy();
       element.addEventListener('show-auth-required', loggedInErrorSpy);
       element.addDraftAtLine();
       assert.isTrue(loggedInErrorSpy.called);
@@ -211,7 +206,7 @@
     });
 
     test('displayLine class added called when displayLine is true', () => {
-      const spy = sandbox.spy(element, '_computeContainerClass');
+      const spy = sinon.spy(element, '_computeContainerClass');
       element.displayLine = true;
       assert.isTrue(spy.called);
       assert.isTrue(
@@ -545,7 +540,7 @@
     });
 
     test('_handleTap lineNum', done => {
-      const addDraftStub = sandbox.stub(element, 'addDraftAtLine');
+      const addDraftStub = sinon.stub(element, 'addDraftAtLine');
       const el = document.createElement('div');
       el.className = 'lineNum';
       el.addEventListener('click', e => {
@@ -559,7 +554,7 @@
 
     test('_handleTap context', done => {
       const showContextStub =
-          sandbox.stub(element.$.diffBuilder, 'showContext');
+          sinon.stub(element.$.diffBuilder, 'showContext');
       const el = document.createElement('div');
       el.className = 'showContext';
       el.addEventListener('click', e => {
@@ -574,8 +569,9 @@
       const content = document.createElement('div');
       const lineEl = document.createElement('div');
 
-      const selectStub = sandbox.stub(element, '_selectLine');
-      sandbox.stub(element.$.diffBuilder, 'getLineElByChild', () => lineEl);
+      const selectStub = sinon.stub(element, '_selectLine');
+      sinon.stub(element.$.diffBuilder, 'getLineElByChild')
+          .callsFake(() => lineEl);
 
       content.className = 'content';
       content.addEventListener('click', e => {
@@ -642,16 +638,16 @@
       element.patchRange = {};
 
       fakeLineEl = {
-        getAttribute: sandbox.stub().returns(42),
+        getAttribute: sinon.stub().returns(42),
         classList: {
-          contains: sandbox.stub().returns(true),
+          contains: sinon.stub().returns(true),
         },
       };
     });
 
     test('addDraftAtLine', () => {
-      sandbox.stub(element, '_selectLine');
-      sandbox.stub(element, '_createComment');
+      sinon.stub(element, '_selectLine');
+      sinon.stub(element, '_createComment');
       element.addDraftAtLine(fakeLineEl);
       assert.isTrue(element._createComment
           .calledWithExactly(fakeLineEl, 42));
@@ -659,9 +655,9 @@
 
     test('addDraftAtLine on an edit', () => {
       element.patchRange.basePatchNum = element.EDIT_NAME;
-      sandbox.stub(element, '_selectLine');
-      sandbox.stub(element, '_createComment');
-      const alertSpy = sandbox.spy();
+      sinon.stub(element, '_selectLine');
+      sinon.stub(element, '_createComment');
+      const alertSpy = sinon.spy();
       element.addEventListener('show-alert', alertSpy);
       element.addDraftAtLine(fakeLineEl);
       assert.isTrue(alertSpy.called);
@@ -671,9 +667,9 @@
     test('addDraftAtLine on an edit base', () => {
       element.patchRange.patchNum = element.EDIT_NAME;
       element.patchRange.basePatchNum = element.PARENT_NAME;
-      sandbox.stub(element, '_selectLine');
-      sandbox.stub(element, '_createComment');
-      const alertSpy = sandbox.spy();
+      sinon.stub(element, '_selectLine');
+      sinon.stub(element, '_createComment');
+      const alertSpy = sinon.spy();
       element.addEventListener('show-alert', alertSpy);
       element.addDraftAtLine(fakeLineEl);
       assert.isTrue(alertSpy.called);
@@ -695,7 +691,7 @@
       });
 
       test('change in preferences re-renders diff', () => {
-        sandbox.stub(element, '_renderDiffTable');
+        sinon.stub(element, '_renderDiffTable');
         element.prefs = Object.assign(
             {}, MINIMAL_PREFS, {time_format: 'HHMM_12'});
         element.flushDebouncer('renderDiffTable');
@@ -703,7 +699,7 @@
       });
 
       test('adding/removing property in preferences re-renders diff', () => {
-        const stub = sandbox.stub(element, '_renderDiffTable');
+        const stub = sinon.stub(element, '_renderDiffTable');
         const newPrefs1 = Object.assign({}, MINIMAL_PREFS,
             {line_wrapping: true});
         element.prefs = newPrefs1;
@@ -720,7 +716,7 @@
 
       test('change in preferences does not re-renders diff with ' +
           'noRenderOnPrefsChange', () => {
-        sandbox.stub(element, '_renderDiffTable');
+        sinon.stub(element, '_renderDiffTable');
         element.noRenderOnPrefsChange = true;
         element.prefs = Object.assign(
             {}, MINIMAL_PREFS, {time_format: 'HHMM_12'});
@@ -778,13 +774,13 @@
 
     setup(() => {
       element = basicFixture.instantiate();
-      renderStub = sandbox.stub(element.$.diffBuilder, 'render',
+      renderStub = sinon.stub(element.$.diffBuilder, 'render').callsFake(
           () => {
             element.$.diffBuilder.dispatchEvent(
                 new CustomEvent('render', {bubbles: true, composed: true}));
             return Promise.resolve({});
           });
-      sandbox.stub(element, 'getDiffLength').returns(10000);
+      sinon.stub(element, 'getDiffLength').returns(10000);
       element.diff = getMockDiffResponse();
       element.noRenderOnPrefsChange = true;
     });
@@ -834,7 +830,7 @@
 
     test('unsetting', () => {
       element.blame = [];
-      const setBlameSpy = sandbox.spy(element.$.diffBuilder, 'setBlame');
+      const setBlameSpy = sinon.spy(element.$.diffBuilder, 'setBlame');
       element.classList.add('showBlame');
       element.blame = null;
       assert.isTrue(setBlameSpy.calledWithExactly(null));
@@ -935,7 +931,7 @@
     setup(() => {
       element = basicFixture.instantiate();
       element.prefs = {};
-      renderStub = sandbox.stub(element.$.diffBuilder, 'render')
+      renderStub = sinon.stub(element.$.diffBuilder, 'render')
           .returns(new Promise(() => {}));
     });
 
@@ -1143,7 +1139,7 @@
   test('`render` event has contentRendered field in detail', done => {
     element = basicFixture.instantiate();
     element.prefs = {};
-    sandbox.stub(element.$.diffBuilder, 'render')
+    sinon.stub(element.$.diffBuilder, 'render')
         .returns(Promise.resolve());
     element.addEventListener('render', event => {
       assert.isTrue(event.detail.contentRendered);
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.js
index cc0f92b..9145b19 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.js
@@ -36,7 +36,7 @@
 
 suite('gr-patch-range-select tests', () => {
   let element;
-  let sandbox;
+
   let commentApiWrapper;
 
   function getInfo(revisions) {
@@ -48,8 +48,6 @@
   }
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     stub('gr-rest-api-interface', {
       getDiffComments() { return Promise.resolve({}); },
       getDiffRobotComments() { return Promise.resolve({}); },
@@ -66,8 +64,6 @@
     return commentApiWrapper.loadComments();
   });
 
-  teardown(() => sandbox.restore());
-
   test('enabled/disabled options', () => {
     const patchRange = {
       basePatchNum: 'PARENT',
@@ -190,7 +186,7 @@
     element.basePatchNum = 'PARENT';
     flushAsynchronousOperations();
 
-    sandbox.stub(element, '_computeBaseDropdownContent');
+    sinon.stub(element, '_computeBaseDropdownContent');
 
     // Should be recomputed for each available patch
     element.set('patchNum', 1);
@@ -217,7 +213,7 @@
         flushAsynchronousOperations();
 
         // Should be recomputed for each available patch
-        sandbox.stub(element, '_computeBaseDropdownContent');
+        sinon.stub(element, '_computeBaseDropdownContent');
         assert.equal(element._computeBaseDropdownContent.callCount, 0);
         commentApiWrapper.loadComments().then()
             .then(() => {
@@ -245,7 +241,7 @@
     flushAsynchronousOperations();
 
     // Should be recomputed for each available patch
-    sandbox.stub(element, '_computePatchDropdownContent');
+    sinon.stub(element, '_computePatchDropdownContent');
     element.set('basePatchNum', 1);
     assert.equal(element._computePatchDropdownContent.callCount, 1);
   });
@@ -269,7 +265,7 @@
     flushAsynchronousOperations();
 
     // Should be recomputed for each available patch
-    sandbox.stub(element, '_computePatchDropdownContent');
+    sinon.stub(element, '_computePatchDropdownContent');
     assert.equal(element._computePatchDropdownContent.callCount, 0);
     commentApiWrapper.loadComments().then()
         .then(() => {
@@ -396,7 +392,7 @@
   });
 
   test('patch-range-change fires', () => {
-    const handler = sandbox.stub();
+    const handler = sinon.stub();
     element.basePatchNum = 1;
     element.patchNum = 3;
     element.addEventListener('patch-range-change', handler);
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.js
index 59cb817..2ce0afa 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.js
@@ -25,7 +25,6 @@
 
 suite('gr-ranged-comment-layer', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     const initialCommentRanges = [
@@ -67,35 +66,24 @@
       },
     ];
 
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.commentRanges = initialCommentRanges;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('annotate', () => {
-    let sandbox;
     let el;
     let line;
     let annotateElementStub;
     const lineNumberEl = document.createElement('td');
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
-      annotateElementStub = sandbox.stub(GrAnnotation, 'annotateElement');
+      annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
       el = document.createElement('div');
       el.setAttribute('data-side', 'left');
       line = new GrDiffLine(GrDiffLine.Type.BOTH);
       line.text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit,';
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('type=Remove no-comment', () => {
       line.type = GrDiffLine.Type.REMOVE;
       line.beforeNumber = 40;
@@ -195,7 +183,7 @@
   test('_handleCommentRangesChange hovering', () => {
     const notifyStub = sinon.stub();
     element.addListener(notifyStub);
-    const updateRangesMapSpy = sandbox.spy(element, '_updateRangesMap');
+    const updateRangesMapSpy = sinon.spy(element, '_updateRangesMap');
 
     element.set(['commentRanges', 1, 'hovering'], true);
 
@@ -242,7 +230,7 @@
   test('_handleCommentRangesChange mixed actions', () => {
     const notifyStub = sinon.stub();
     element.addListener(notifyStub);
-    const updateRangesMapSpy = sandbox.spy(element, '_updateRangesMap');
+    const updateRangesMapSpy = sinon.spy(element, '_updateRangesMap');
 
     element.set(['commentRanges', 1, 'hovering'], true);
     assert.isTrue(updateRangesMapSpy.callCount === 1);
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.js b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.js
index e2e901d..81cf0d6 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.js
@@ -29,17 +29,12 @@
 suite('gr-selection-action-box', () => {
   let container;
   let element;
-  let sandbox;
 
   setup(() => {
     container = basicFixture.instantiate();
     element = container.querySelector('gr-selection-action-box');
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(element, 'dispatchEvent');
-  });
 
-  teardown(() => {
-    sandbox.restore();
+    sinon.stub(element, 'dispatchEvent');
   });
 
   test('ignores regular keys', () => {
@@ -53,8 +48,8 @@
     setup(() => {
       e = {
         button: 0,
-        preventDefault: sandbox.stub(),
-        stopPropagation: sandbox.stub(),
+        preventDefault: sinon.stub(),
+        stopPropagation: sinon.stub(),
       };
     });
 
@@ -80,11 +75,11 @@
 
     setup(() => {
       target = container.querySelector('.target');
-      sandbox.stub(container, 'getBoundingClientRect').returns(
+      sinon.stub(container, 'getBoundingClientRect').returns(
           {top: 1, bottom: 2, left: 3, right: 4, width: 50, height: 6});
-      sandbox.stub(element, '_getTargetBoundingRect').returns(
+      sinon.stub(element, '_getTargetBoundingRect').returns(
           {top: 42, bottom: 20, left: 30, right: 40, width: 100, height: 60});
-      sandbox.stub(element.$.tooltip, 'getBoundingClientRect').returns(
+      sinon.stub(element.$.tooltip, 'getBoundingClientRect').returns(
           {width: 10, height: 10});
     });
 
@@ -113,9 +108,9 @@
     });
 
     test('uses document.createRange', () => {
-      sandbox.spy(document, 'createRange');
+      sinon.spy(document, 'createRange');
       element._getTargetBoundingRect.restore();
-      sandbox.spy(element, '_getTargetBoundingRect');
+      sinon.spy(element, '_getTargetBoundingRect');
       element.placeAbove(target.firstChild);
       assert.isTrue(document.createRange.called);
     });
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.js
index f3d1a40..e83d4b64 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.js
@@ -24,7 +24,6 @@
 const basicFixture = fixtureFromElement('gr-syntax-layer');
 
 suite('gr-syntax-layer tests', () => {
-  let sandbox;
   let diff;
   let element;
   const lineNumberEl = document.createElement('td');
@@ -49,18 +48,13 @@
   }
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     diff = getMockDiffResponse();
     element.diff = diff;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('annotate without range does nothing', () => {
-    const annotationSpy = sandbox.spy(GrAnnotation, 'annotateElement');
+    const annotationSpy = sinon.spy(GrAnnotation, 'annotateElement');
     const el = document.createElement('div');
     el.textContent = 'Etiam dui, blandit wisi.';
     const line = new GrDiffLine(GrDiffLine.Type.REMOVE);
@@ -77,7 +71,7 @@
     const length = 3;
     const className = 'foobar';
 
-    const annotationSpy = sandbox.spy(GrAnnotation, 'annotateElement');
+    const annotationSpy = sinon.spy(GrAnnotation, 'annotateElement');
     const el = document.createElement('div');
     el.textContent = str;
     const line = new GrDiffLine(GrDiffLine.Type.REMOVE);
@@ -104,7 +98,7 @@
     const length = 3;
     const className = 'foobar';
 
-    const annotationSpy = sandbox.spy(GrAnnotation, 'annotateElement');
+    const annotationSpy = sinon.spy(GrAnnotation, 'annotateElement');
     const el = document.createElement('div');
     el.textContent = str;
     const line = new GrDiffLine(GrDiffLine.Type.REMOVE);
@@ -127,7 +121,7 @@
       meta_b: {content_type: 'application/json'},
       content: [],
     };
-    const processNextSpy = sandbox.spy(element, '_processNextLine');
+    const processNextSpy = sinon.spy(element, '_processNextLine');
 
     const processPromise = element.process();
 
@@ -145,7 +139,7 @@
       meta_b: {content_type: 'application/not-a-real-language'},
       content: [],
     };
-    const processNextSpy = sandbox.spy(element, '_processNextLine');
+    const processNextSpy = sinon.spy(element, '_processNextLine');
 
     const processPromise = element.process();
 
@@ -158,9 +152,9 @@
   });
 
   test('process while disabled does nothing', done => {
-    const processNextSpy = sandbox.spy(element, '_processNextLine');
+    const processNextSpy = sinon.spy(element, '_processNextLine');
     element.enabled = false;
-    const loadHLJSSpy = sandbox.spy(element, '_loadHLJS');
+    const loadHLJSSpy = sinon.spy(element, '_loadHLJS');
 
     const processPromise = element.process();
 
@@ -179,9 +173,9 @@
 
     const mockHLJS = getMockHLJS();
     const highlightSpy = sinon.spy(mockHLJS, 'highlight');
-    sandbox.stub(element.$.libLoader, 'getHLJS',
+    sinon.stub(element.$.libLoader, 'getHLJS').callsFake(
         () => Promise.resolve(mockHLJS));
-    const processNextSpy = sandbox.spy(element, '_processNextLine');
+    const processNextSpy = sinon.spy(element, '_processNextLine');
     const processPromise = element.process();
 
     processPromise.then(() => {
@@ -244,7 +238,7 @@
   });
 
   test('_diffChanged calls cancel', () => {
-    const cancelSpy = sandbox.spy(element, '_diffChanged');
+    const cancelSpy = sinon.spy(element, '_diffChanged');
     element.diff = {content: []};
     assert.isTrue(cancelSpy.called);
   });
@@ -379,7 +373,7 @@
   });
 
   test('_rangesFromString cache same syntax markers', () => {
-    sandbox.spy(element, '_rangesFromElement');
+    sinon.spy(element, '_rangesFromElement');
     const str =
       '<span class="gr-diff gr-syntax gr-syntax-keyword">public</span>';
     const cacheMap = new Map();
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.js b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.js
index c7d419a..c2a3f3d 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.js
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.js
@@ -32,20 +32,15 @@
 suite('gr-documentation-search tests', () => {
   let element;
   let documentationSearches;
-  let sandbox;
+
   let value;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(page, 'show');
+    sinon.stub(page, 'show');
     element = basicFixture.instantiate();
     counter = 0;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('list with searches for documentation', () => {
     setup(done => {
       documentationSearches = _.times(26, documentationGenerator);
@@ -75,10 +70,10 @@
     });
 
     test('_paramsChanged', done => {
-      sandbox.stub(
+      sinon.stub(
           element.$.restAPI,
-          'getDocumentationSearches',
-          () => Promise.resolve(documentationSearches));
+          'getDocumentationSearches')
+          .callsFake(() => Promise.resolve(documentationSearches));
       const value = {
         filter: 'test',
       };
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.js
index f1ee264..8269b81 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.js
@@ -25,25 +25,22 @@
 
 suite('gr-edit-controls tests', () => {
   let element;
-  let sandbox;
+
   let showDialogSpy;
   let closeDialogSpy;
   let queryStub;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.change = {_number: '42'};
-    showDialogSpy = sandbox.spy(element, '_showDialog');
-    closeDialogSpy = sandbox.spy(element, '_closeDialog');
-    sandbox.stub(element, '_hideAllDialogs');
-    queryStub = sandbox.stub(element.$.restAPI, 'queryChangeFiles')
+    showDialogSpy = sinon.spy(element, '_showDialog');
+    closeDialogSpy = sinon.spy(element, '_closeDialog');
+    sinon.stub(element, '_hideAllDialogs');
+    queryStub = sinon.stub(element.$.restAPI, 'queryChangeFiles')
         .returns(Promise.resolve([]));
     flushAsynchronousOperations();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('all actions exist', () => {
     // We take 1 away from the total found, due to an extra button being
     // added for the file uploads (browse).
@@ -58,8 +55,8 @@
 
     setup(() => {
       navStubs = [
-        sandbox.stub(GerritNav, 'getEditUrlForDiff'),
-        sandbox.stub(GerritNav, 'navigateToRelativeUrl'),
+        sinon.stub(GerritNav, 'getEditUrlForDiff'),
+        sinon.stub(GerritNav, 'navigateToRelativeUrl'),
       ];
       openAutoCcmplete = element.$.openDialog.querySelector('gr-autocomplete');
     });
@@ -117,8 +114,8 @@
     let deleteAutocomplete;
 
     setup(() => {
-      navStub = sandbox.stub(GerritNav, 'navigateToChange');
-      deleteStub = sandbox.stub(element.$.restAPI, 'deleteFileInChangeEdit');
+      navStub = sinon.stub(GerritNav, 'navigateToChange');
+      deleteStub = sinon.stub(element.$.restAPI, 'deleteFileInChangeEdit');
       deleteAutocomplete =
           element.$.deleteDialog.querySelector('gr-autocomplete');
     });
@@ -201,8 +198,8 @@
       '.newPathInput';
 
     setup(() => {
-      navStub = sandbox.stub(GerritNav, 'navigateToChange');
-      renameStub = sandbox.stub(element.$.restAPI, 'renameFileInChangeEdit');
+      navStub = sinon.stub(GerritNav, 'navigateToChange');
+      renameStub = sinon.stub(element.$.restAPI, 'renameFileInChangeEdit');
       renameAutocomplete =
           element.$.renameDialog.querySelector('gr-autocomplete');
     });
@@ -294,8 +291,8 @@
     let restoreStub;
 
     setup(() => {
-      navStub = sandbox.stub(GerritNav, 'navigateToChange');
-      restoreStub = sandbox.stub(element.$.restAPI, 'restoreFileInChangeEdit');
+      navStub = sinon.stub(GerritNav, 'navigateToChange');
+      restoreStub = sinon.stub(element.$.restAPI, 'restoreFileInChangeEdit');
     });
 
     test('restore hidden by default', () => {
@@ -358,8 +355,8 @@
     let fileStub;
 
     setup(() => {
-      navStub = sandbox.stub(GerritNav, 'navigateToChange');
-      fileStub = sandbox.stub(element.$.restAPI, 'saveFileUploadChangeEdit');
+      navStub = sinon.stub(GerritNav, 'navigateToChange');
+      fileStub = sinon.stub(element.$.restAPI, 'saveFileUploadChangeEdit');
     });
 
     test('_handleUploadConfirm', () => {
@@ -395,7 +392,7 @@
   });
 
   test('_getDialogFromEvent', () => {
-    const spy = sandbox.spy(element, '_getDialogFromEvent');
+    const spy = sinon.spy(element, '_getDialogFromEvent');
     element.addEventListener('tap', element._getDialogFromEvent);
 
     MockInteractions.tap(element.$.openDialog);
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.js
index 735a8f5..8a1c186 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.js
@@ -24,18 +24,15 @@
 
 suite('gr-edit-file-controls tests', () => {
   let element;
-  let sandbox;
+
   let fileActionHandler;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
-    fileActionHandler = sandbox.stub();
+    fileActionHandler = sinon.stub();
     element.addEventListener('file-action-tap', fileActionHandler);
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('open tap emits event', () => {
     const actions = element.$.actions;
     element.filePath = 'foo';
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js
index d9f9606..618f595 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js
@@ -23,7 +23,7 @@
 
 suite('gr-editor-view tests', () => {
   let element;
-  let sandbox;
+
   let savePathStub;
   let saveFileStub;
   let changeDetailStub;
@@ -39,15 +39,13 @@
       getLoggedIn() { return Promise.resolve(true); },
       getEditPreferences() { return Promise.resolve({}); },
     });
-    sandbox = sinon.sandbox.create();
-    element = basicFixture.instantiate();
-    savePathStub = sandbox.stub(element.$.restAPI, 'renameFileInChangeEdit');
-    saveFileStub = sandbox.stub(element.$.restAPI, 'saveChangeEdit');
-    changeDetailStub = sandbox.stub(element.$.restAPI, 'getDiffChangeDetail');
-    navigateStub = sandbox.stub(element, '_viewEditInChangeView');
-  });
 
-  teardown(() => { sandbox.restore(); });
+    element = basicFixture.instantiate();
+    savePathStub = sinon.stub(element.$.restAPI, 'renameFileInChangeEdit');
+    saveFileStub = sinon.stub(element.$.restAPI, 'saveChangeEdit');
+    changeDetailStub = sinon.stub(element.$.restAPI, 'getDiffChangeDetail');
+    navigateStub = sinon.stub(element, '_viewEditInChangeView');
+  });
 
   suite('_paramsChanged', () => {
     test('incorrect view returns immediately', () => {
@@ -58,7 +56,7 @@
 
     test('good params proceed', () => {
       changeDetailStub.returns(Promise.resolve({}));
-      const fileStub = sandbox.stub(element, '_getFileData', () => {
+      const fileStub = sinon.stub(element, '_getFileData').callsFake(() => {
         element._content = 'text';
         element._newContent = 'text';
         element._type = 'application/octet-stream';
@@ -106,7 +104,7 @@
   });
 
   test('reacts to content-change event', () => {
-    const storeStub = sandbox.spy(element.$.storage, 'setEditableContentItem');
+    const storeStub = sinon.spy(element.$.storage, 'setEditableContentItem');
     element._newContent = 'test';
     element.$.editorEndpoint.dispatchEvent(new CustomEvent('content-change', {
       bubbles: true, composed: true,
@@ -138,10 +136,10 @@
     });
 
     test('file modification and save, !ok response', () => {
-      const saveSpy = sandbox.spy(element, '_saveEdit');
-      const eraseStub = sandbox.stub(element.$.storage,
+      const saveSpy = sinon.spy(element, '_saveEdit');
+      const eraseStub = sinon.stub(element.$.storage,
           'eraseEditableContentItem');
-      const alertStub = sandbox.stub(element, '_showAlert');
+      const alertStub = sinon.stub(element, '_showAlert');
       saveFileStub.returns(Promise.resolve({ok: false}));
       element._newContent = newText;
       flushAsynchronousOperations();
@@ -169,8 +167,8 @@
     });
 
     test('file modification and save', () => {
-      const saveSpy = sandbox.spy(element, '_saveEdit');
-      const alertStub = sandbox.stub(element, '_showAlert');
+      const saveSpy = sinon.spy(element, '_saveEdit');
+      const alertStub = sinon.stub(element, '_showAlert');
       saveFileStub.returns(Promise.resolve({ok: true}));
       element._newContent = newText;
       flushAsynchronousOperations();
@@ -196,7 +194,7 @@
     });
 
     test('file modification and close', () => {
-      const closeSpy = sandbox.spy(element, '_handleCloseTap');
+      const closeSpy = sinon.spy(element, '_handleCloseTap');
       element._newContent = newText;
       flushAsynchronousOperations();
 
@@ -214,11 +212,11 @@
       element._newContent = 'initial';
       element._content = 'initial';
       element._type = 'initial';
-      sandbox.stub(element.$.storage, 'getEditableContentItem').returns(null);
+      sinon.stub(element.$.storage, 'getEditableContentItem').returns(null);
     });
 
     test('res.ok', () => {
-      sandbox.stub(element.$.restAPI, 'getFileContent')
+      sinon.stub(element.$.restAPI, 'getFileContent')
           .returns(Promise.resolve({
             ok: true,
             type: 'text/javascript',
@@ -234,7 +232,7 @@
     });
 
     test('!res.ok', () => {
-      sandbox.stub(element.$.restAPI, 'getFileContent')
+      sinon.stub(element.$.restAPI, 'getFileContent')
           .returns(Promise.resolve({}));
 
       // Ensure no data is set with a bad response.
@@ -246,7 +244,7 @@
     });
 
     test('content is undefined', () => {
-      sandbox.stub(element.$.restAPI, 'getFileContent')
+      sinon.stub(element.$.restAPI, 'getFileContent')
           .returns(Promise.resolve({
             ok: true,
             type: 'text/javascript',
@@ -260,7 +258,7 @@
     });
 
     test('content and type is undefined', () => {
-      sandbox.stub(element.$.restAPI, 'getFileContent')
+      sinon.stub(element.$.restAPI, 'getFileContent')
           .returns(Promise.resolve({
             ok: true,
           }));
@@ -285,7 +283,7 @@
 
   test('_viewEditInChangeView respects _patchNum', () => {
     navigateStub.restore();
-    const navStub = sandbox.stub(GerritNav, 'navigateToChange');
+    const navStub = sinon.stub(GerritNav, 'navigateToChange');
     element._patchNum = element.EDIT_NAME;
     element._viewEditInChangeView();
     assert.equal(navStub.lastCall.args[1], element.EDIT_NAME);
@@ -304,8 +302,8 @@
     suite('_handleSaveShortcut', () => {
       let saveStub;
       setup(() => {
-        handleSpy = sandbox.spy(element, '_handleSaveShortcut');
-        saveStub = sandbox.stub(element, '_saveEdit');
+        handleSpy = sinon.spy(element, '_handleSaveShortcut');
+        saveStub = sinon.stub(element, '_saveEdit');
       });
 
       test('save enabled', () => {
@@ -342,16 +340,16 @@
 
   suite('gr-storage caching', () => {
     test('local edit exists', () => {
-      sandbox.stub(element.$.storage, 'getEditableContentItem')
+      sinon.stub(element.$.storage, 'getEditableContentItem')
           .returns({message: 'pending edit'});
-      sandbox.stub(element.$.restAPI, 'getFileContent')
+      sinon.stub(element.$.restAPI, 'getFileContent')
           .returns(Promise.resolve({
             ok: true,
             type: 'text/javascript',
             content: 'old content',
           }));
 
-      const alertStub = sandbox.stub();
+      const alertStub = sinon.stub();
       element.addEventListener('show-alert', alertStub);
 
       return element._getFileData(1, 'test', 1).then(() => {
@@ -365,16 +363,16 @@
     });
 
     test('local edit exists, is same as remote edit', () => {
-      sandbox.stub(element.$.storage, 'getEditableContentItem')
+      sinon.stub(element.$.storage, 'getEditableContentItem')
           .returns({message: 'pending edit'});
-      sandbox.stub(element.$.restAPI, 'getFileContent')
+      sinon.stub(element.$.restAPI, 'getFileContent')
           .returns(Promise.resolve({
             ok: true,
             type: 'text/javascript',
             content: 'pending edit',
           }));
 
-      const alertStub = sandbox.stub();
+      const alertStub = sinon.stub();
       element.addEventListener('show-alert', alertStub);
 
       return element._getFileData(1, 'test', 1).then(() => {
diff --git a/polygerrit-ui/app/elements/gr-app_test.js b/polygerrit-ui/app/elements/gr-app_test.js
index 86c24f7..4c5e965 100644
--- a/polygerrit-ui/app/elements/gr-app_test.js
+++ b/polygerrit-ui/app/elements/gr-app_test.js
@@ -24,17 +24,15 @@
 const basicFixture = fixtureFromTemplate(html`<gr-app id="app"></gr-app>`);
 
 suite('gr-app tests', () => {
-  let sandbox;
   let element;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(appContext.reportingService, 'appStarted');
+    sinon.stub(appContext.reportingService, 'appStarted');
     stub('gr-account-dropdown', {
       _getTopContent: sinon.stub(),
     });
     stub('gr-router', {
-      start: sandbox.stub(),
+      start: sinon.stub(),
     });
     stub('gr-rest-api-interface', {
       getAccount() { return Promise.resolve({}); },
@@ -58,10 +56,6 @@
     flush(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   const appElement = () => element.$['app-element'];
 
   test('reporting', () => {
diff --git a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.js b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.js
index 87ec2f5..c3552cb 100644
--- a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.js
@@ -23,11 +23,9 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-admin-api tests', () => {
-  let sandbox;
   let adminApi;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     let plugin;
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
@@ -37,7 +35,6 @@
 
   teardown(() => {
     adminApi = null;
-    sandbox.restore();
   });
 
   test('exists', () => {
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.js b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.js
index 9881954..ea7bdc3 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.js
@@ -34,18 +34,12 @@
 suite('gr-attribute-helper tests', () => {
   let element;
   let instance;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     instance = new GrAttributeHelper(element);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('resolved on value change from undefined', () => {
     const promise = instance.get('fooBar').then(value => {
       assert.equal(value, 'foo! bar!');
@@ -64,7 +58,7 @@
   });
 
   test('bind', () => {
-    const stub = sandbox.stub();
+    const stub = sinon.stub();
     element.fooBar = 'bar foo';
     const unbind = instance.bind('fooBar', stub);
     element.fooBar = 'partridge in a foo tree';
diff --git a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.js b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.js
index e98ada3..a2b92ec 100644
--- a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.js
@@ -32,25 +32,20 @@
   ];
 
   let instance;
-  let sandbox;
+
   let hook;
   let hookInternal;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     let plugin;
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
     instance = new GrDomHooksManager(plugin);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('placeholder', () => {
     setup(()=>{
-      sandbox.stub(GrDomHook.prototype, '_createPlaceholder');
+      sinon.stub(GrDomHook.prototype, '_createPlaceholder');
       hookInternal = instance.getDomHook('foo-bar');
       hook = hookInternal.getPublicAPI();
     });
@@ -88,7 +83,7 @@
     });
 
     test('onAttached', () => {
-      const onAttachedSpy = sandbox.spy();
+      const onAttachedSpy = sinon.spy();
       hook.onAttached(onAttachedSpy);
       const [el1, el2] = [
         document.createElement(hook.getModuleName()),
@@ -101,7 +96,7 @@
     });
 
     test('onDetached', () => {
-      const onDetachedSpy = sandbox.spy();
+      const onDetachedSpy = sinon.spy();
       hook.onDetached(onDetachedSpy);
       const [el1, el2] = [
         document.createElement(hook.getModuleName()),
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.js
index 17ada2c..72c73b7 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.js
@@ -48,17 +48,17 @@
 
 suite('gr-endpoint-decorator', () => {
   let container;
-  let sandbox;
+
   let plugin;
   let decorationHook;
   let decorationHookWithSlot;
   let replacementHook;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     resetPlugins();
     container = basicFixture.instantiate();
-    sandbox.stub(pluginEndpoints, 'importUrl', url => Promise.resolve());
+    sinon.stub(pluginEndpoints, 'importUrl')
+        .callsFake( url => Promise.resolve());
     pluginApi.install(p => plugin = p, '0.1',
         'http://some/plugin/url.html');
     // Decoration
@@ -77,7 +77,6 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     resetPlugins();
   });
 
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.js b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.js
index e268c5f..e56278f 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.js
@@ -36,18 +36,12 @@
 suite('gr-event-helper tests', () => {
   let element;
   let instance;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     instance = new GrEventHelper(element);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('onTap()', done => {
     instance.onTap(() => {
       done();
@@ -56,7 +50,7 @@
   });
 
   test('onTap() cancel', () => {
-    const tapStub = sandbox.stub();
+    const tapStub = sinon.stub();
     addListener(element.parentElement, 'tap', tapStub);
     instance.onTap(() => false);
     MockInteractions.tap(element);
@@ -65,7 +59,7 @@
   });
 
   test('onClick() cancel', () => {
-    const tapStub = sandbox.stub();
+    const tapStub = sinon.stub();
     element.parentElement.addEventListener('click', tapStub);
     instance.onTap(() => false);
     MockInteractions.tap(element);
@@ -88,7 +82,7 @@
   });
 
   test('captureTap() cancels tap()', () => {
-    const tapStub = sandbox.stub();
+    const tapStub = sinon.stub();
     addListener(element.parentElement, 'tap', tapStub);
     instance.captureTap(() => false);
     MockInteractions.tap(element);
@@ -97,7 +91,7 @@
   });
 
   test('captureClick() cancels click()', () => {
-    const tapStub = sandbox.stub();
+    const tapStub = sinon.stub();
     element.addEventListener('click', tapStub);
     instance.captureTap(() => false);
     MockInteractions.tap(element);
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.js
index 790c088..6659207 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.js
@@ -32,7 +32,6 @@
 suite('gr-external-style integration tests', () => {
   const TEST_URL = 'http://some.com/plugins/url.html';
 
-  let sandbox;
   let element;
   let plugin;
 
@@ -45,7 +44,7 @@
 
   const createElement = () => {
     element = basicFixture.instantiate();
-    sandbox.spy(element, '_applyStyle');
+    sinon.spy(element, '_applyStyle');
   };
 
   /**
@@ -67,14 +66,13 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(pluginEndpoints, 'importUrl', url => Promise.resolve());
-    sandbox.stub(pluginLoader, 'awaitPluginsLoaded')
+    sinon.stub(pluginEndpoints, 'importUrl')
+        .callsFake( url => Promise.resolve());
+    sinon.stub(pluginLoader, 'awaitPluginsLoaded')
         .returns(Promise.resolve());
   });
 
   teardown(() => {
-    sandbox.restore();
     resetPlugins();
     document.body.querySelectorAll('custom-style')
         .forEach(style => style.remove());
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.js b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.js
index b77d33e..83e0a84 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.js
@@ -23,20 +23,15 @@
 
 suite('gr-plugin-host tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(document.body, 'appendChild');
-  });
 
-  teardown(() => {
-    sandbox.restore();
+    sinon.stub(document.body, 'appendChild');
   });
 
   test('load plugins should be called', () => {
-    sandbox.stub(pluginLoader, 'loadPlugins');
+    sinon.stub(pluginLoader, 'loadPlugins');
     element.config = {
       plugin: {
         html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
@@ -50,7 +45,7 @@
   });
 
   test('theme plugins should be loaded if enabled', () => {
-    sandbox.stub(pluginLoader, 'loadPlugins');
+    sinon.stub(pluginLoader, 'loadPlugins');
     element.config = {
       default_theme: 'gerrit-theme.html',
       plugin: {
@@ -65,10 +60,10 @@
   });
 
   test('skip theme if preloaded', () => {
-    sandbox.stub(pluginLoader, 'isPluginPreloaded')
+    sinon.stub(pluginLoader, 'isPluginPreloaded')
         .withArgs('preloaded:gerrit-theme')
         .returns(true);
-    sandbox.stub(pluginLoader, 'loadPlugins');
+    sinon.stub(pluginLoader, 'loadPlugins');
     element.config = {
       default_theme: '/oof',
       plugin: {},
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.js
index bbb08d1..f2d83e8 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.js
@@ -22,21 +22,15 @@
 
 suite('gr-plugin-popup tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     stub('gr-overlay', {
-      open: sandbox.stub().returns(Promise.resolve()),
-      close: sandbox.stub(),
+      open: sinon.stub().returns(Promise.resolve()),
+      close: sinon.stub(),
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('exists', () => {
     assert.isOk(element);
   });
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.js
index 42ebf98..9312332 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.js
@@ -40,24 +40,18 @@
   let container;
   let instance;
   let plugin;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
     container = containerFixture.instantiate();
-    sandbox.stub(plugin, 'hook').returns({
+    sinon.stub(plugin, 'hook').returns({
       getLastAttached() {
         return Promise.resolve(container);
       },
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('manual', () => {
     setup(() => {
       instance = new GrPopupInterface(plugin);
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.js b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.js
index 4c8e1e5..c7f1b06 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.js
@@ -29,11 +29,9 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-repo-api tests', () => {
-  let sandbox;
   let repoApi;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     let plugin;
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
@@ -43,7 +41,6 @@
 
   teardown(() => {
     repoApi = null;
-    sandbox.restore();
   });
 
   test('exists', () => {
@@ -51,8 +48,8 @@
   });
 
   test('works', done => {
-    const attachedStub = sandbox.stub();
-    const tapStub = sandbox.stub();
+    const attachedStub = sinon.stub();
+    const tapStub = sinon.stub();
     repoApi
         .createCommand('foo', attachedStub)
         .onTap(tapStub);
diff --git a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.js b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.js
index 236d1f0..82d58fe 100644
--- a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.js
@@ -31,11 +31,9 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-settings-api tests', () => {
-  let sandbox;
   let settingsApi;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     let plugin;
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
@@ -45,7 +43,6 @@
 
   teardown(() => {
     settingsApi = null;
-    sandbox.restore();
   });
 
   test('exists', () => {
diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.js b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.js
index 9671708..5ccda28 100644
--- a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.js
@@ -36,11 +36,9 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-styles-api tests', () => {
-  let sandbox;
   let stylesApi;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     let plugin;
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
@@ -50,7 +48,6 @@
 
   teardown(() => {
     stylesApi = null;
-    sandbox.restore();
   });
 
   test('exists', () => {
@@ -63,14 +60,12 @@
   });
 
   suite('GrStyleObject tests', () => {
-    let sandbox;
     let stylesApi;
     let displayInlineStyle;
     let displayNoneStyle;
     let elementsToRemove;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       let plugin;
       pluginApi.install(p => { plugin = p; }, '0.1',
           'http://test.com/plugins/testplugin/static/test.js');
@@ -89,7 +84,7 @@
         element.remove();
       });
       elementsToRemove = null;
-      sandbox.restore();
+      sinon.restore();
     });
 
     function createNestedElements(parentElement) {
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.js b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.js
index 7b1e4ab..8a70303 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.js
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.js
@@ -30,11 +30,9 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-theme-api tests', () => {
-  let sandbox;
   let theme;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     let plugin;
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
@@ -43,7 +41,6 @@
 
   teardown(() => {
     theme = null;
-    sandbox.restore();
   });
 
   test('exists', () => {
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.js
index 45b75d4..4a62bab 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.js
@@ -25,7 +25,6 @@
   let element;
   let account;
   let config;
-  let sandbox;
 
   function valueOf(title) {
     const sections = dom(element.root).querySelectorAll('section');
@@ -39,7 +38,6 @@
   }
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     account = {
       _account_id: 123,
       name: 'user name',
@@ -61,10 +59,6 @@
     element.loadData().then(() => { flush(done); });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('basic account info render', () => {
     assert.isFalse(element._loading);
 
@@ -134,17 +128,17 @@
     let statusStub;
 
     setup(() => {
-      nameChangedSpy = sandbox.spy(element, '_nameChanged');
-      usernameChangedSpy = sandbox.spy(element, '_usernameChanged');
-      statusChangedSpy = sandbox.spy(element, '_statusChanged');
+      nameChangedSpy = sinon.spy(element, '_nameChanged');
+      usernameChangedSpy = sinon.spy(element, '_usernameChanged');
+      statusChangedSpy = sinon.spy(element, '_statusChanged');
       element.set('_serverConfig',
           {auth: {editable_account_fields: ['FULL_NAME', 'USER_NAME']}});
 
-      nameStub = sandbox.stub(element.$.restAPI, 'setAccountName',
+      nameStub = sinon.stub(element.$.restAPI, 'setAccountName').callsFake(
           name => Promise.resolve());
-      usernameStub = sandbox.stub(element.$.restAPI, 'setAccountUsername',
-          username => Promise.resolve());
-      statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
+      usernameStub = sinon.stub(element.$.restAPI, 'setAccountUsername')
+          .callsFake(username => Promise.resolve());
+      statusStub = sinon.stub(element.$.restAPI, 'setAccountStatus').callsFake(
           status => Promise.resolve());
     });
 
@@ -219,16 +213,16 @@
     let statusStub;
 
     setup(() => {
-      nameChangedSpy = sandbox.spy(element, '_nameChanged');
-      statusChangedSpy = sandbox.spy(element, '_statusChanged');
+      nameChangedSpy = sinon.spy(element, '_nameChanged');
+      statusChangedSpy = sinon.spy(element, '_statusChanged');
       element.set('_serverConfig',
           {auth: {editable_account_fields: ['FULL_NAME']}});
 
-      nameStub = sandbox.stub(element.$.restAPI, 'setAccountName',
+      nameStub = sinon.stub(element.$.restAPI, 'setAccountName').callsFake(
           name => Promise.resolve());
-      statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
+      statusStub = sinon.stub(element.$.restAPI, 'setAccountStatus').callsFake(
           status => Promise.resolve());
-      sandbox.stub(element.$.restAPI, 'setAccountUsername',
+      sinon.stub(element.$.restAPI, 'setAccountUsername').callsFake(
           username => Promise.resolve());
     });
 
@@ -264,11 +258,11 @@
     let statusStub;
 
     setup(() => {
-      statusChangedSpy = sandbox.spy(element, '_statusChanged');
+      statusChangedSpy = sinon.spy(element, '_statusChanged');
       element.set('_serverConfig',
           {auth: {editable_account_fields: []}});
 
-      statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
+      statusStub = sinon.stub(element.$.restAPI, 'setAccountStatus').callsFake(
           status => Promise.resolve());
     });
 
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.js b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.js
index e051ef7..3fca9d2 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.js
@@ -23,11 +23,9 @@
 suite('gr-change-table-editor tests', () => {
   let element;
   let columns;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
 
     columns = [
       'Subject',
@@ -46,10 +44,6 @@
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('renders', () => {
     const rows = element.shadowRoot
         .querySelector('tbody').querySelectorAll('tr');
@@ -112,8 +106,8 @@
   });
 
   test('_handleCheckboxContainerClick relays taps to checkboxes', () => {
-    sandbox.stub(element, '_handleNumberCheckboxClick');
-    sandbox.stub(element, '_handleTargetClick');
+    sinon.stub(element, '_handleNumberCheckboxClick');
+    sinon.stub(element, '_handleTargetClick');
 
     MockInteractions.tap(
         element.shadowRoot
@@ -129,7 +123,7 @@
   });
 
   test('_handleNumberCheckboxClick', () => {
-    sandbox.spy(element, '_handleNumberCheckboxClick');
+    sinon.spy(element, '_handleNumberCheckboxClick');
 
     MockInteractions
         .tap(element.shadowRoot
@@ -145,7 +139,7 @@
   });
 
   test('_handleTargetClick', () => {
-    sandbox.spy(element, '_handleTargetClick');
+    sinon.spy(element, '_handleTargetClick');
     assert.include(element.displayedColumns, 'Assignee');
     MockInteractions
         .tap(element.shadowRoot
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.js b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.js
index e9967b7..b1b8b61 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.js
@@ -22,7 +22,7 @@
 
 suite('gr-edit-preferences tests', () => {
   let element;
-  let sandbox;
+
   let editPreferences;
 
   function valueOf(title, fieldsetid) {
@@ -63,12 +63,10 @@
     });
 
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
+
     return element.loadData();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('renders', () => {
     // Rendered with the expected preferences selected.
     assert.equal(valueOf('Tab width', 'editPreferences')
@@ -94,7 +92,7 @@
   });
 
   test('save changes', () => {
-    sandbox.stub(element.$.restAPI, 'saveEditPreferences')
+    sinon.stub(element.$.restAPI, 'saveEditPreferences')
         .returns(Promise.resolve());
     const showTabsCheckbox = valueOf('Show tabs', 'editPreferences')
         .firstElementChild;
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.js
index 7752ca7..5281e17 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.js
@@ -75,8 +75,8 @@
   test('remove key', done => {
     const lastKey = keys[Object.keys(keys)[1]];
 
-    const saveStub = sinon.stub(element.$.restAPI, 'deleteAccountGPGKey',
-        () => Promise.resolve());
+    const saveStub = sinon.stub(element.$.restAPI, 'deleteAccountGPGKey')
+        .callsFake(() => Promise.resolve());
 
     assert.equal(element._keysToRemove.length, 0);
     assert.isFalse(element.hasUnsavedChanges);
@@ -131,7 +131,7 @@
       },
     };
 
-    const addStub = sinon.stub(element.$.restAPI, 'addAccountGPGKey',
+    const addStub = sinon.stub(element.$.restAPI, 'addAccountGPGKey').callsFake(
         () => Promise.resolve(newKeyObject));
 
     element._newKey = newKeyString;
@@ -156,7 +156,7 @@
   test('add invalid key', done => {
     const newKeyString = 'not even close to valid';
 
-    const addStub = sinon.stub(element.$.restAPI, 'addAccountGPGKey',
+    const addStub = sinon.stub(element.$.restAPI, 'addAccountGPGKey').callsFake(
         () => Promise.reject(new Error('error')));
 
     element._newKey = newKeyString;
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.js b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.js
index dec2928..bfd42ac 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.js
@@ -23,12 +23,10 @@
 const basicFixture = fixtureFromElement('gr-group-list');
 
 suite('gr-group-list tests', () => {
-  let sandbox;
   let element;
   let groups;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     groups = [{
       url: 'some url',
       options: {},
@@ -57,8 +55,6 @@
     element.loadData().then(() => { flush(done); });
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('renders', () => {
     const rows = Array.from(
         dom(element.root).querySelectorAll('tbody tr'));
@@ -80,7 +76,7 @@
   });
 
   test('_computeGroupPath', () => {
-    let urlStub = sandbox.stub(GerritNav, 'getUrlForGroup',
+    let urlStub = sinon.stub(GerritNav, 'getUrlForGroup').callsFake(
         () => '/admin/groups/e2cd66f88a2db4d391ac068a92d987effbe872f5');
 
     let group = {
@@ -96,7 +92,7 @@
 
     urlStub.restore();
 
-    urlStub = sandbox.stub(GerritNav, 'getUrlForGroup',
+    urlStub = sinon.stub(GerritNav, 'getUrlForGroup').callsFake(
         () => '/admin/groups/user/test');
 
     group = {
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.js b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.js
index 388ac96..920ad48 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.js
@@ -43,7 +43,8 @@
     const nextPassword = 'the new password';
     let generateResolve;
     const generateStub = sinon.stub(element.$.restAPI,
-        'generateAccountHttpPassword', () => new Promise(resolve => {
+        'generateAccountHttpPassword')
+        .callsFake(() => new Promise(resolve => {
           generateResolve = resolve;
         }));
 
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.js b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.js
index cd5e685..e01c58b 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.js
@@ -23,7 +23,7 @@
 
 suite('gr-identities tests', () => {
   let element;
-  let sandbox;
+
   const ids = [
     {
       identity: 'username:john',
@@ -41,8 +41,6 @@
   ];
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
-
     stub('gr-rest-api-interface', {
       getExternalIds() { return Promise.resolve(ids); },
     });
@@ -52,10 +50,6 @@
     element.loadData().then(() => { flush(done); });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('renders', () => {
     const rows = Array.from(
         dom(element.root).querySelectorAll('tbody tr'));
@@ -98,7 +92,7 @@
 
   test('delete id', done => {
     element._idName = 'mailto:gerrit2@example.com';
-    const loadDataStub = sandbox.stub(element, 'loadData');
+    const loadDataStub = sinon.stub(element, 'loadData');
     element._handleDeleteItemConfirm().then(() => {
       assert.isTrue(loadDataStub.called);
       done();
@@ -108,7 +102,7 @@
   test('_handleDeleteItem opens modal', () => {
     const deleteBtn =
         dom(element.root).querySelector('.deleteButton');
-    const deleteItem = sandbox.stub(element, '_handleDeleteItem');
+    const deleteItem = sinon.stub(element, '_handleDeleteItem');
     MockInteractions.tap(deleteBtn);
     assert.isTrue(deleteItem.called);
   });
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.js
index e6f06df..468ef57 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.js
@@ -22,11 +22,10 @@
 suite('gr-registration-dialog tests', () => {
   let element;
   let account;
-  let sandbox;
+
   let _listeners;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     _listeners = {};
 
     account = {
@@ -67,7 +66,6 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     for (const eventType in _listeners) {
       if (_listeners.hasOwnProperty(eventType)) {
         element.removeEventListener(eventType, _listeners[eventType]);
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
index f328cda..c0ec344 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
@@ -28,7 +28,6 @@
   let account;
   let preferences;
   let config;
-  let sandbox;
 
   function valueOf(title, fieldsetid) {
     const sections = element.$[fieldsetid].querySelectorAll('section');
@@ -51,12 +50,11 @@
   }
 
   function stubAddAccountEmail(statusCode) {
-    return sandbox.stub(element.$.restAPI, 'addAccountEmail',
+    return sinon.stub(element.$.restAPI, 'addAccountEmail').callsFake(
         () => Promise.resolve({status: statusCode}));
   }
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     account = {
       _account_id: 123,
       name: 'user name',
@@ -100,10 +98,6 @@
     element._loadingPromise.then(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('theme changing', () => {
     window.localStorage.removeItem('dark-theme');
     assert.isFalse(window.localStorage.getItem('dark-theme') === 'true');
@@ -119,7 +113,7 @@
   });
 
   test('calls the title-change event', () => {
-    const titleChangedStub = sandbox.stub();
+    const titleChangedStub = sinon.stub();
 
     // Create a new view.
     const newElement = document.createElement('gr-settings-view');
@@ -346,7 +340,7 @@
   });
 
   test('emails are loaded without emailToken', () => {
-    sandbox.stub(element.$.emailEditor, 'loadData');
+    sinon.stub(element.$.emailEditor, 'loadData');
     element.params = {};
     element.attached();
     assert.isTrue(element.$.emailEditor.loadData.calledOnce);
@@ -356,7 +350,7 @@
     let newColumns = ['Owner', 'Project', 'Branch'];
     element._localChangeTableColumns = newColumns.slice(0);
     element._showNumber = false;
-    const cloneStub = sandbox.stub(element, '_cloneChangeTableColumns');
+    const cloneStub = sinon.stub(element, '_cloneChangeTableColumns');
     element._handleSaveChangeTable();
     assert.isTrue(cloneStub.calledOnce);
     assert.deepEqual(element.prefs.change_table, newColumns);
@@ -400,7 +394,7 @@
   });
 
   test('test that reset button is called', () => {
-    const overlayOpen = sandbox.stub(element, '_handleResetMenuButton');
+    const overlayOpen = sinon.stub(element, '_handleResetMenuButton');
 
     MockInteractions.tap(element.$.resetMenu);
 
@@ -484,11 +478,13 @@
     let resolveConfirm;
 
     setup(() => {
-      sandbox.stub(element.$.emailEditor, 'loadData');
-      sandbox.stub(
+      sinon.stub(element.$.emailEditor, 'loadData');
+      sinon.stub(
           element.$.restAPI,
-          'confirmEmail',
-          () => new Promise(resolve => { resolveConfirm = resolve; }));
+          'confirmEmail')
+          .callsFake(
+              () => new Promise(
+                  resolve => { resolveConfirm = resolve; }));
       element.params = {emailToken: 'foo'};
       element.attached();
     });
@@ -511,7 +507,7 @@
     });
 
     test('show-alert is fired when email is confirmed', done => {
-      sandbox.spy(element, 'dispatchEvent');
+      sinon.spy(element, 'dispatchEvent');
       element._loadingPromise.then(() => {
         assert.equal(
             element.dispatchEvent.lastCall.args[0].type, 'show-alert');
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.js
index 445199b..d4a0372 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.js
@@ -66,8 +66,8 @@
   test('remove key', done => {
     const lastKey = keys[1];
 
-    const saveStub = sinon.stub(element.$.restAPI, 'deleteAccountSSHKey',
-        () => Promise.resolve());
+    const saveStub = sinon.stub(element.$.restAPI, 'deleteAccountSSHKey')
+        .callsFake(() => Promise.resolve());
 
     assert.equal(element._keysToRemove.length, 0);
     assert.isFalse(element.hasUnsavedChanges);
@@ -117,7 +117,7 @@
       valid: true,
     };
 
-    const addStub = sinon.stub(element.$.restAPI, 'addAccountSSHKey',
+    const addStub = sinon.stub(element.$.restAPI, 'addAccountSSHKey').callsFake(
         () => Promise.resolve(newKeyObject));
 
     element._newKey = newKeyString;
@@ -142,7 +142,7 @@
   test('add invalid key', done => {
     const newKeyString = 'not even close to valid';
 
-    const addStub = sinon.stub(element.$.restAPI, 'addAccountSSHKey',
+    const addStub = sinon.stub(element.$.restAPI, 'addAccountSSHKey').callsFake(
         () => Promise.reject(new Error('error')));
 
     element._newKey = newKeyString;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.js b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.js
index 0ec3481..396145b 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.js
@@ -21,7 +21,6 @@
 const basicFixture = fixtureFromElement('gr-account-entry');
 
 suite('gr-account-entry tests', () => {
-  let sandbox;
   let element;
 
   const suggestion1 = {
@@ -41,12 +40,8 @@
 
   setup(done => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-    return flush(done);
-  });
 
-  teardown(() => {
-    sandbox.restore();
+    return flush(done);
   });
 
   suite('stubbed values for querySuggestions', () => {
@@ -62,7 +57,7 @@
   test('account-text-changed fired when input text changed and allowAnyInput',
       () => {
         // Spy on query, as that is called when _updateSuggestions proceeds.
-        const changeStub = sandbox.stub();
+        const changeStub = sinon.stub();
         element.allowAnyInput = true;
         element.querySuggestions = input => Promise.resolve([]);
         element.addEventListener('account-text-changed', changeStub);
@@ -75,7 +70,7 @@
   test('account-text-changed not fired when input text changed without ' +
       'allowAnyInput', () => {
     // Spy on query, as that is called when _updateSuggestions proceeds.
-    const changeStub = sandbox.stub();
+    const changeStub = sinon.stub();
     element.querySuggestions = input => Promise.resolve([]);
     element.addEventListener('account-text-changed', changeStub);
     element.$.input.text = 'a';
@@ -84,7 +79,7 @@
 
   test('setText', () => {
     // Spy on query, as that is called when _updateSuggestions proceeds.
-    const suggestSpy = sandbox.spy(element.$.input, 'query');
+    const suggestSpy = sinon.spy(element.$.input, 'query');
     element.setText('test text');
     flushAsynchronousOperations();
 
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.js
index e42907b..554953e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.js
@@ -23,23 +23,17 @@
 
 suite('gr-account-link tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
     });
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('computed fields', () => {
     const url = 'test/url';
-    const urlStub = sandbox.stub(GerritNav, 'getUrlForOwner').returns(url);
+    const urlStub = sinon.stub(GerritNav, 'getUrlForOwner').returns(url);
     const account = {
       email: 'email',
       username: 'username',
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.js
index c7635cf..b26f468 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.js
@@ -49,7 +49,7 @@
 
   let existingAccount1;
   let existingAccount2;
-  let sandbox;
+
   let element;
   let suggestionsProvider;
 
@@ -58,7 +58,6 @@
   }
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     existingAccount1 = makeAccount();
     existingAccount2 = makeAccount();
 
@@ -71,10 +70,6 @@
     element.suggestionsProvider = suggestionsProvider;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('account entry only appears when editable', () => {
     element.readonly = false;
     assert.isFalse(element.$.entry.hasAttribute('hidden'));
@@ -84,7 +79,7 @@
 
   test('addition and removal of account/group chips', () => {
     flushAsynchronousOperations();
-    sandbox.stub(element, '_computeRemovable').returns(true);
+    sinon.stub(element, '_computeRemovable').returns(true);
     // Existing accounts are listed.
     let chips = getChips();
     assert.equal(chips.length, 2);
@@ -180,14 +175,15 @@
         _account_id: 25,
       },
     ];
-    sandbox.stub(suggestionsProvider, 'getSuggestions')
+    sinon.stub(suggestionsProvider, 'getSuggestions')
         .returns(Promise.resolve(originalSuggestions));
-    sandbox.stub(suggestionsProvider, 'makeSuggestionItem', suggestion => {
-      return {
-        name: suggestion.email,
-        value: suggestion._account_id,
-      };
-    });
+    sinon.stub(suggestionsProvider, 'makeSuggestionItem')
+        .callsFake( suggestion => {
+          return {
+            name: suggestion.email,
+            value: suggestion._account_id,
+          };
+        });
 
     element._getSuggestions().then(suggestions => {
       // Default is no filtering.
@@ -242,13 +238,13 @@
     element.allowAnyInput = true;
     flushAsynchronousOperations();
 
-    const getTextStub = sandbox.stub(element.$.entry, 'getText');
+    const getTextStub = sinon.stub(element.$.entry, 'getText');
     getTextStub.onFirstCall().returns('');
     getTextStub.onSecondCall().returns('test');
     getTextStub.onThirdCall().returns('test@test');
 
     // When entry is empty, return true.
-    const clearStub = sandbox.stub(element.$.entry, 'clear');
+    const clearStub = sinon.stub(element.$.entry, 'clear');
     assert.isTrue(element.submitEntryText());
     assert.isFalse(clearStub.called);
 
@@ -366,11 +362,12 @@
       },
     ];
     const getSuggestionsStub =
-        sandbox.stub(suggestionsProvider, 'getSuggestions')
+        sinon.stub(suggestionsProvider, 'getSuggestions')
             .returns(Promise.resolve(suggestions));
 
     const makeSuggestionItemStub =
-        sandbox.stub(suggestionsProvider, 'makeSuggestionItem', item => item);
+        sinon.stub(suggestionsProvider, 'makeSuggestionItem')
+            .callsFake( item => item);
 
     const input = element.$.entry.$.input;
 
@@ -399,11 +396,12 @@
       },
     ];
     const getSuggestionsStub =
-        sandbox.stub(suggestionsProvider, 'getSuggestions')
+        sinon.stub(suggestionsProvider, 'getSuggestions')
             .returns(Promise.resolve(suggestions));
 
     const makeSuggestionItemStub =
-        sandbox.stub(suggestionsProvider, 'makeSuggestionItem', item => item);
+        sinon.stub(suggestionsProvider, 'makeSuggestionItem')
+            .callsFake( item => item);
 
     const input = element.$.entry.$.input;
 
@@ -422,7 +420,7 @@
   test('skip suggestion on empty', done => {
     element.skipSuggestOnEmpty = true;
     const getSuggestionsStub =
-        sandbox.stub(suggestionsProvider, 'getSuggestions')
+        sinon.stub(suggestionsProvider, 'getSuggestions')
             .returns(Promise.resolve([]));
 
     const input = element.$.entry.$.input;
@@ -450,7 +448,7 @@
     });
 
     test('toasts on invalid email', () => {
-      const toastHandler = sandbox.stub();
+      const toastHandler = sinon.stub();
       element.addEventListener('show-alert', toastHandler);
       element._handleAdd({detail: {value: 'test'}});
       assert.isTrue(toastHandler.called);
@@ -473,8 +471,8 @@
   suite('keyboard interactions', () => {
     test('backspace at text input start removes last account', done => {
       const input = element.$.entry.$.input;
-      sandbox.stub(input, '_updateSuggestions');
-      sandbox.stub(element, '_computeRemovable').returns(true);
+      sinon.stub(input, '_updateSuggestions');
+      sinon.stub(element, '_computeRemovable').returns(true);
       flush(() => {
         // Next line is a workaround for Firefox not moving cursor
         // on input field update
@@ -504,10 +502,10 @@
         MockInteractions.focus(input.$.input);
         flushAsynchronousOperations();
         const chips = element.accountChips;
-        const chipsOneSpy = sandbox.spy(chips[1], 'focus');
+        const chipsOneSpy = sinon.spy(chips[1], 'focus');
         MockInteractions.pressAndReleaseKeyOn(input.$.input, 37); // Left
         assert.isTrue(chipsOneSpy.called);
-        const chipsZeroSpy = sandbox.spy(chips[0], 'focus');
+        const chipsZeroSpy = sinon.spy(chips[0], 'focus');
         MockInteractions.pressAndReleaseKeyOn(chips[1], 37); // Left
         assert.isTrue(chipsZeroSpy.called);
         MockInteractions.pressAndReleaseKeyOn(chips[0], 37); // Left
@@ -521,8 +519,8 @@
     test('delete', done => {
       element.accounts = [makeAccount(), makeAccount()];
       flush(() => {
-        const focusSpy = sandbox.spy(element.accountChips[1], 'focus');
-        const removeSpy = sandbox.spy(element, 'removeAccount');
+        const focusSpy = sinon.spy(element.accountChips[1], 'focus');
+        const removeSpy = sinon.spy(element, 'removeAccount');
         MockInteractions.pressAndReleaseKeyOn(
             element.accountChips[0], 8); // Backspace
         assert.isTrue(focusSpy.called);
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.js
index 54e2a5f..f76d070 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.js
@@ -22,10 +22,8 @@
 
 suite('gr-autocomplete-dropdown', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.open();
     element.suggestions = [
@@ -35,7 +33,6 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     if (element.isOpen) element.close();
   });
 
@@ -46,7 +43,7 @@
   });
 
   test('escape key', done => {
-    const closeSpy = sandbox.spy(element, 'close');
+    const closeSpy = sinon.spy(element, 'close');
     MockInteractions.pressAndReleaseKeyOn(element, 27);
     flushAsynchronousOperations();
     assert.isTrue(closeSpy.called);
@@ -54,8 +51,8 @@
   });
 
   test('tab key', () => {
-    const handleTabSpy = sandbox.spy(element, '_handleTab');
-    const itemSelectedStub = sandbox.stub();
+    const handleTabSpy = sinon.spy(element, '_handleTab');
+    const itemSelectedStub = sinon.stub();
     element.addEventListener('item-selected', itemSelectedStub);
     MockInteractions.pressAndReleaseKeyOn(element, 9);
     assert.isTrue(handleTabSpy.called);
@@ -68,8 +65,8 @@
   });
 
   test('enter key', () => {
-    const handleEnterSpy = sandbox.spy(element, '_handleEnter');
-    const itemSelectedStub = sandbox.stub();
+    const handleEnterSpy = sinon.spy(element, '_handleEnter');
+    const itemSelectedStub = sinon.stub();
     element.addEventListener('item-selected', itemSelectedStub);
     MockInteractions.pressAndReleaseKeyOn(element, 13);
     assert.isTrue(handleEnterSpy.called);
@@ -82,7 +79,7 @@
 
   test('down key', () => {
     element.isHidden = true;
-    const nextSpy = sandbox.spy(element.$.cursor, 'next');
+    const nextSpy = sinon.spy(element.$.cursor, 'next');
     MockInteractions.pressAndReleaseKeyOn(element, 40);
     assert.isFalse(nextSpy.called);
     assert.equal(element.$.cursor.index, 0);
@@ -94,7 +91,7 @@
 
   test('up key', () => {
     element.isHidden = true;
-    const prevSpy = sandbox.spy(element.$.cursor, 'previous');
+    const prevSpy = sinon.spy(element.$.cursor, 'previous');
     MockInteractions.pressAndReleaseKeyOn(element, 38);
     assert.isFalse(prevSpy.called);
     assert.equal(element.$.cursor.index, 0);
@@ -107,7 +104,7 @@
   });
 
   test('tapping selects item', () => {
-    const itemSelectedStub = sandbox.stub();
+    const itemSelectedStub = sinon.stub();
     element.addEventListener('item-selected', itemSelectedStub);
 
     MockInteractions.tap(element.$.suggestions.querySelectorAll('li')[1]);
@@ -119,7 +116,7 @@
   });
 
   test('tapping child still selects item', () => {
-    const itemSelectedStub = sandbox.stub();
+    const itemSelectedStub = sinon.stub();
     element.addEventListener('item-selected', itemSelectedStub);
 
     MockInteractions.tap(element.$.suggestions.querySelectorAll('li')[0]
@@ -132,7 +129,7 @@
   });
 
   test('updated suggestions resets cursor stops', () => {
-    const resetStopsSpy = sandbox.spy(element, '_resetCursorStops');
+    const resetStopsSpy = sinon.spy(element, '_resetCursorStops');
     element.suggestions = [];
     assert.isTrue(resetStopsSpy.called);
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.js
index eb05b90..e9753c9 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.js
@@ -25,7 +25,7 @@
 
 suite('gr-autocomplete tests', () => {
   let element;
-  let sandbox;
+
   const focusOnInput = element => {
     MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
         'enter');
@@ -33,16 +33,11 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('renders', () => {
     let promise;
-    const queryStub = sandbox.spy(input => promise = Promise.resolve([
+    const queryStub = sinon.spy(input => promise = Promise.resolve([
       {name: input + ' 0', value: 0},
       {name: input + ' 1', value: 1},
       {name: input + ' 2', value: 2},
@@ -76,7 +71,7 @@
   test('selectAll', done => {
     flush(() => {
       const nativeInput = element._nativeInput;
-      const selectionStub = sandbox.stub(nativeInput, 'setSelectionRange');
+      const selectionStub = sinon.stub(nativeInput, 'setSelectionRange');
 
       element.selectAll();
       assert.isFalse(selectionStub.called);
@@ -90,7 +85,7 @@
 
   test('esc key behavior', done => {
     let promise;
-    const queryStub = sandbox.spy(() => promise = Promise.resolve([
+    const queryStub = sinon.spy(() => promise = Promise.resolve([
       {name: 'blah', value: 123},
     ]));
     element.query = queryStub;
@@ -103,7 +98,7 @@
     promise.then(() => {
       assert.isFalse(element.$.suggestions.isHidden);
 
-      const cancelHandler = sandbox.spy();
+      const cancelHandler = sinon.spy();
       element.addEventListener('cancel', cancelHandler);
 
       MockInteractions.pressAndReleaseKeyOn(element.$.input, 27, null, 'esc');
@@ -119,7 +114,7 @@
 
   test('emits commit and handles cursor movement', done => {
     let promise;
-    const queryStub = sandbox.spy(input => promise = Promise.resolve([
+    const queryStub = sinon.spy(input => promise = Promise.resolve([
       {name: input + ' 0', value: 0},
       {name: input + ' 1', value: 1},
       {name: input + ' 2', value: 2},
@@ -136,7 +131,7 @@
     promise.then(() => {
       assert.isFalse(element.$.suggestions.isHidden);
 
-      const commitHandler = sandbox.spy();
+      const commitHandler = sinon.spy();
       element.addEventListener('commit', commitHandler);
 
       assert.equal(element.$.suggestions.$.cursor.index, 0);
@@ -169,7 +164,7 @@
 
   test('clear-on-commit behavior (off)', done => {
     let promise;
-    const queryStub = sandbox.spy(() => {
+    const queryStub = sinon.spy(() => {
       promise = Promise.resolve([{name: 'suggestion', value: 0}]);
       return promise;
     });
@@ -178,7 +173,7 @@
     element.text = 'blah';
 
     promise.then(() => {
-      const commitHandler = sandbox.spy();
+      const commitHandler = sinon.spy();
       element.addEventListener('commit', commitHandler);
 
       MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
@@ -192,7 +187,7 @@
 
   test('clear-on-commit behavior (on)', done => {
     let promise;
-    const queryStub = sandbox.spy(() => {
+    const queryStub = sinon.spy(() => {
       promise = Promise.resolve([{name: 'suggestion', value: 0}]);
       return promise;
     });
@@ -202,7 +197,7 @@
     element.clearOnCommit = true;
 
     promise.then(() => {
-      const commitHandler = sandbox.spy();
+      const commitHandler = sinon.spy();
       element.addEventListener('commit', commitHandler);
 
       MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
@@ -215,7 +210,7 @@
   });
 
   test('threshold guards the query', () => {
-    const queryStub = sandbox.spy(() => Promise.resolve([]));
+    const queryStub = sinon.spy(() => Promise.resolve([]));
     element.query = queryStub;
     element.threshold = 2;
     focusOnInput(element);
@@ -226,9 +221,9 @@
   });
 
   test('noDebounce=false debounces the query', () => {
-    const queryStub = sandbox.spy(() => Promise.resolve([]));
+    const queryStub = sinon.spy(() => Promise.resolve([]));
     let callback;
-    const debounceStub = sandbox.stub(element, 'debounce',
+    const debounceStub = sinon.stub(element, 'debounce').callsFake(
         (name, cb) => { callback = cb; });
     element.query = queryStub;
     element.noDebounce = false;
@@ -255,7 +250,7 @@
 
   test('when focused', done => {
     let promise;
-    const queryStub = sandbox.stub()
+    const queryStub = sinon.stub()
         .returns(promise = Promise.resolve([
           {name: 'suggestion', value: 0},
         ]));
@@ -274,7 +269,7 @@
 
   test('when not focused', done => {
     let promise;
-    const queryStub = sandbox.stub()
+    const queryStub = sinon.stub()
         .returns(promise = Promise.resolve([
           {name: 'suggestion', value: 0},
         ]));
@@ -291,7 +286,7 @@
 
   test('suggestions should not carry over', done => {
     let promise;
-    const queryStub = sandbox.stub()
+    const queryStub = sinon.stub()
         .returns(promise = Promise.resolve([
           {name: 'suggestion', value: 0},
         ]));
@@ -309,7 +304,7 @@
 
   test('multi completes only the last part of the query', done => {
     let promise;
-    const queryStub = sandbox.stub()
+    const queryStub = sinon.stub()
         .returns(promise = Promise.resolve([
           {name: 'suggestion', value: 0},
         ]));
@@ -319,7 +314,7 @@
     element.multi = true;
 
     promise.then(() => {
-      const commitHandler = sandbox.spy();
+      const commitHandler = sinon.spy();
       element.addEventListener('commit', commitHandler);
 
       MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
@@ -334,9 +329,9 @@
   test('tabComplete flag functions', () => {
     // commitHandler checks for the commit event, whereas commitSpy checks for
     // the _commit function of the element.
-    const commitHandler = sandbox.spy();
+    const commitHandler = sinon.spy();
     element.addEventListener('commit', commitHandler);
-    const commitSpy = sandbox.spy(element, '_commit');
+    const commitSpy = sinon.spy(element, '_commit');
     element._focused = true;
 
     element._suggestions = ['tunnel snakes rule!'];
@@ -385,8 +380,8 @@
   });
 
   test('_focused flag shows/hides the suggestions', () => {
-    const openStub = sandbox.stub(element.$.suggestions, 'open');
-    const closedStub = sandbox.stub(element.$.suggestions, 'close');
+    const openStub = sinon.stub(element.$.suggestions, 'open');
+    const closedStub = sinon.stub(element.$.suggestions, 'close');
     element._suggestions = ['hello', 'its me'];
     assert.isFalse(openStub.called);
     assert.isTrue(closedStub.calledOnce);
@@ -399,7 +394,7 @@
 
   test('_handleInputCommit with autocomplete hidden does nothing without' +
         'without allowNonSuggestedValues', () => {
-    const commitStub = sandbox.stub(element, '_commit');
+    const commitStub = sinon.stub(element, '_commit');
     element.$.suggestions.isHidden = true;
     element._handleInputCommit();
     assert.isFalse(commitStub.called);
@@ -407,7 +402,7 @@
 
   test('_handleInputCommit with autocomplete hidden with' +
         'allowNonSuggestedValues', () => {
-    const commitStub = sandbox.stub(element, '_commit');
+    const commitStub = sinon.stub(element, '_commit');
     element.allowNonSuggestedValues = true;
     element.$.suggestions.isHidden = true;
     element._handleInputCommit();
@@ -415,7 +410,7 @@
   });
 
   test('_handleInputCommit with autocomplete open calls commit', () => {
-    const commitStub = sandbox.stub(element, '_commit');
+    const commitStub = sinon.stub(element, '_commit');
     element.$.suggestions.isHidden = false;
     element._handleInputCommit();
     assert.isTrue(commitStub.calledOnce);
@@ -423,7 +418,7 @@
 
   test('_handleInputCommit with autocomplete open calls commit' +
         'with allowNonSuggestedValues', () => {
-    const commitStub = sandbox.stub(element, '_commit');
+    const commitStub = sinon.stub(element, '_commit');
     element.allowNonSuggestedValues = true;
     element.$.suggestions.isHidden = false;
     element._handleInputCommit();
@@ -432,7 +427,7 @@
 
   test('issue 8655', () => {
     function makeSuggestion(s) { return {name: s, text: s, value: s}; }
-    const keydownSpy = sandbox.spy(element, '_handleKeydown');
+    const keydownSpy = sinon.spy(element, '_handleKeydown');
     element.setText('file:');
     element._suggestions =
         [makeSuggestion('file:'), makeSuggestion('-file:')];
@@ -455,12 +450,12 @@
     let focusSpy;
 
     setup(() => {
-      commitSpy = sandbox.spy(element, '_commit');
+      commitSpy = sinon.spy(element, '_commit');
     });
 
     test('enter does not call focus', () => {
       element._suggestions = ['sugar bombs'];
-      focusSpy = sandbox.spy(element, 'focus');
+      focusSpy = sinon.spy(element, 'focus');
       MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
           'enter');
       flushAsynchronousOperations();
@@ -471,8 +466,8 @@
     });
 
     test('tab in input, tabComplete = true', () => {
-      focusSpy = sandbox.spy(element, 'focus');
-      const commitHandler = sandbox.stub();
+      focusSpy = sinon.spy(element, 'focus');
+      const commitHandler = sinon.stub();
       element.addEventListener('commit', commitHandler);
       element.tabComplete = true;
       element._suggestions = ['tunnel snakes drool'];
@@ -487,7 +482,7 @@
 
     test('tab in input, tabComplete = false', () => {
       element._suggestions = ['sugar bombs'];
-      focusSpy = sandbox.spy(element, 'focus');
+      focusSpy = sinon.spy(element, 'focus');
       MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
       flushAsynchronousOperations();
 
@@ -501,7 +496,7 @@
       element._focused = true;
       // When tabComplete is false, do not focus.
       element.tabComplete = false;
-      focusSpy = sandbox.spy(element, 'focus');
+      focusSpy = sinon.spy(element, 'focus');
       flush$0();
       assert.isFalse(element.$.suggestions.isHidden);
 
@@ -518,7 +513,7 @@
       element._focused = true;
       // When tabComplete is true, focus.
       element.tabComplete = true;
-      focusSpy = sandbox.spy(element, 'focus');
+      focusSpy = sinon.spy(element, 'focus');
       flush$0();
       assert.isFalse(element.$.suggestions.isHidden);
 
@@ -532,7 +527,7 @@
     });
 
     test('tap on suggestion commits, does not call focus', () => {
-      focusSpy = sandbox.spy(element, 'focus');
+      focusSpy = sinon.spy(element, 'focus');
       element._focused = true;
       element._suggestions = [{name: 'first suggestion'}];
       flush$0();
@@ -548,7 +543,7 @@
   });
 
   test('input-keydown event fired', () => {
-    const listener = sandbox.spy();
+    const listener = sinon.spy();
     element.addEventListener('input-keydown', listener);
     MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
     flushAsynchronousOperations();
@@ -556,8 +551,8 @@
   });
 
   test('enter with modifier does not complete', () => {
-    const handleSpy = sandbox.spy(element, '_handleKeydown');
-    const commitStub = sandbox.stub(element, '_handleInputCommit');
+    const handleSpy = sinon.spy(element, '_handleKeydown');
+    const commitStub = sinon.stub(element, '_handleInputCommit');
     MockInteractions.pressAndReleaseKeyOn(
         element.$.input, 13, 'ctrl', 'enter');
     assert.isTrue(handleSpy.called);
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js
index 6a3fe36..5e43f90 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js
@@ -23,17 +23,11 @@
 
 suite('gr-avatar tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('methods', () => {
     assert.equal(
         element._buildAvatarURL({
@@ -161,23 +155,22 @@
       element = basicFixture.instantiate();
     });
 
-    test('avatar hidden when account set', () => {
-      flush(() => {
-        assert.isFalse(element.hasAttribute('hidden'));
+    test('avatar hidden when account set', async () => {
+      await flush();
+      assert.isTrue(element.hasAttribute('hidden'));
 
-        element.imageSize = 64;
-        element.account = {
-          _account_id: 123,
-        };
-        // Emulate plugins loaded.
-        pluginLoader.loadPlugins([]);
+      element.imageSize = 64;
+      element.account = {
+        _account_id: 123,
+      };
+      // Emulate plugins loaded.
+      pluginLoader.loadPlugins([]);
 
-        return Promise.all([
-          element.$.restAPI.getConfig(),
-          pluginLoader.awaitPluginsLoaded(),
-        ]).then(() => {
-          assert.isTrue(element.hasAttribute('hidden'));
-        });
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        pluginLoader.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isTrue(element.hasAttribute('hidden'));
       });
     });
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.js b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.js
index 3bad443..4bc3bea 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.js
@@ -35,10 +35,9 @@
 
 suite('gr-button tests', () => {
   let element;
-  let sandbox;
 
   const addSpyOn = function(eventName) {
-    const spy = sandbox.spy();
+    const spy = sinon.spy();
     if (eventName == 'tap') {
       addListener(element, eventName, spy);
     } else {
@@ -49,11 +48,6 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('disabled is set by disabled', () => {
@@ -132,14 +126,14 @@
   // Keycodes: 32 for Space, 13 for Enter.
   for (const key of [32, 13]) {
     test('dispatches click event on keycode ' + key, () => {
-      const tapSpy = sandbox.spy();
+      const tapSpy = sinon.spy();
       element.addEventListener('click', tapSpy);
       MockInteractions.pressAndReleaseKeyOn(element, key);
       assert.isTrue(tapSpy.calledOnce);
     });
 
     test('dispatches no click event with modifier on keycode ' + key, () => {
-      const tapSpy = sandbox.spy();
+      const tapSpy = sinon.spy();
       element.addEventListener('click', tapSpy);
       MockInteractions.pressAndReleaseKeyOn(element, key, 'shift');
       MockInteractions.pressAndReleaseKeyOn(element, key, 'ctrl');
@@ -165,7 +159,7 @@
     // Keycodes: 32 for Space, 13 for Enter.
     for (const key of [32, 13]) {
       test('stops click event on keycode ' + key, () => {
-        const tapSpy = sandbox.spy();
+        const tapSpy = sinon.spy();
         element.addEventListener('click', tapSpy);
         MockInteractions.pressAndReleaseKeyOn(element, key);
         assert.isFalse(tapSpy.called);
@@ -176,7 +170,7 @@
   suite('reporting', () => {
     let reportStub;
     setup(() => {
-      reportStub = sandbox.stub(appContext.reportingService,
+      reportStub = sinon.stub(appContext.reportingService,
           'reportInteraction');
       reportStub.reset();
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js
index 8112e21..770a21c 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js
@@ -33,15 +33,9 @@
 
 suite('gr-change-status tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('WIP', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.js b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.js
index 17c92fc..3837514 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.js
@@ -28,19 +28,13 @@
 suite('gr-comment-thread tests', () => {
   suite('basic test', () => {
     let element;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       stub('gr-rest-api-interface', {
         getLoggedIn() { return Promise.resolve(false); },
       });
-      sandbox = sinon.sandbox.create();
-      element = basicFixture.instantiate();
-    });
 
-    teardown(() => {
-      sandbox.restore();
+      element = basicFixture.instantiate();
     });
 
     test('comments are sorted correctly', () => {
@@ -113,9 +107,9 @@
         updated: '2015-12-25 15:00:20.396000000',
         __draft: true,
       }];
-      const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
-          () => { return {}; });
-      const addDraftStub = sandbox.stub(element, 'addDraft');
+      const commentElStub = sinon.stub(element, '_commentElWithDraftID')
+          .callsFake(() => { return {}; });
+      const addDraftStub = sinon.stub(element, 'addDraft');
 
       element.addOrEditDraft(123);
 
@@ -125,9 +119,9 @@
 
     test('addOrEditDraft w/o edit draft', () => {
       element.comments = [];
-      const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
-          () => { return {}; });
-      const addDraftStub = sandbox.stub(element, 'addDraft');
+      const commentElStub = sinon.stub(element, '_commentElWithDraftID')
+          .callsFake(() => { return {}; });
+      const addDraftStub = sinon.stub(element, 'addDraft');
 
       element.addOrEditDraft(123);
 
@@ -169,7 +163,7 @@
 
     test('setting project name loads the project config', done => {
       const projectName = 'foo/bar/baz';
-      const getProjectStub = sandbox.stub(element.$.restAPI, 'getProjectConfig')
+      const getProjectStub = sinon.stub(element.$.restAPI, 'getProjectConfig')
           .returns(Promise.resolve({}));
       element.projectName = projectName;
       flush(() => {
@@ -184,7 +178,7 @@
       assert.isNotOk(element.shadowRoot
           .querySelector('.pathInfo'));
 
-      sandbox.stub(GerritNav, 'getUrlForDiffById');
+      sinon.stub(GerritNav, 'getUrlForDiffById');
       element.changeNum = 123;
       element.projectName = 'test project';
       element.path = 'path/to/file';
@@ -234,10 +228,8 @@
 
 suite('comment action tests with unresolved thread', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(false); },
       saveDiffDraft() {
@@ -276,14 +268,10 @@
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('reply', () => {
     const commentEl = element.shadowRoot
         .querySelector('gr-comment');
-    const reportStub = sandbox.stub(element.reporting,
+    const reportStub = sinon.stub(element.reporting,
         'recordDraftInteraction');
     assert.ok(commentEl);
 
@@ -301,7 +289,7 @@
   test('quote reply', () => {
     const commentEl = element.shadowRoot
         .querySelector('gr-comment');
-    const reportStub = sandbox.stub(element.reporting,
+    const reportStub = sinon.stub(element.reporting,
         'recordDraftInteraction');
     assert.ok(commentEl);
 
@@ -317,7 +305,7 @@
   });
 
   test('quote reply multiline', () => {
-    const reportStub = sandbox.stub(element.reporting,
+    const reportStub = sinon.stub(element.reporting,
         'recordDraftInteraction');
     element.comments = [{
       author: {
@@ -348,7 +336,7 @@
   });
 
   test('ack', done => {
-    const reportStub = sandbox.stub(element.reporting,
+    const reportStub = sinon.stub(element.reporting,
         'recordDraftInteraction');
     element.changeNum = '42';
     element.patchNum = '1';
@@ -371,7 +359,7 @@
   });
 
   test('done', done => {
-    const reportStub = sandbox.stub(element.reporting,
+    const reportStub = sinon.stub(element.reporting,
         'recordDraftInteraction');
     element.changeNum = '42';
     element.patchNum = '1';
@@ -400,7 +388,7 @@
         .querySelector('gr-comment');
     assert.ok(commentEl);
 
-    const saveOrDiscardStub = sandbox.stub();
+    const saveOrDiscardStub = sinon.stub();
     element.addEventListener('thread-changed', saveOrDiscardStub);
     element.shadowRoot
         .querySelector('gr-comment')._fireSave();
@@ -448,7 +436,7 @@
         'it’s pronouced jiff, not giff'));
     flushAsynchronousOperations();
 
-    const saveOrDiscardStub = sandbox.stub();
+    const saveOrDiscardStub = sinon.stub();
     element.addEventListener('thread-changed', saveOrDiscardStub);
     const draftEl =
         dom(element.root).querySelectorAll('gr-comment')[1];
@@ -481,7 +469,7 @@
         const rootId = element.rootId;
         assert.isOk(rootId);
 
-        const saveOrDiscardStub = sandbox.stub();
+        const saveOrDiscardStub = sinon.stub();
         element.addEventListener('thread-changed', saveOrDiscardStub);
         const draftEl =
         dom(element.root).querySelectorAll('gr-comment')[0];
@@ -815,10 +803,8 @@
 
 suite('comment action tests on resolved comments', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getLoggedIn() { return Promise.resolve(false); },
       saveDiffDraft() {
@@ -855,10 +841,6 @@
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('ack and done should be hidden', () => {
     element.changeNum = '42';
     element.patchNum = '1';
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js
index cfbe836..b227383 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js
@@ -33,7 +33,7 @@
 suite('gr-comment tests', () => {
   suite('basic tests', () => {
     let element;
-    let sandbox;
+
     let openOverlaySpy;
 
     setup(() => {
@@ -51,15 +51,14 @@
         message: 'is this a crossover episode!?',
         updated: '2015-12-08 19:48:33.843000000',
       };
-      sandbox = sinon.sandbox.create();
-      openOverlaySpy = sandbox.spy(element, '_openOverlay');
+
+      openOverlaySpy = sinon.spy(element, '_openOverlay');
     });
 
     teardown(() => {
       openOverlaySpy.getCalls().forEach(call => {
         call.args[0].remove();
       });
-      sandbox.restore();
     });
 
     test('collapsible comments', () => {
@@ -109,8 +108,8 @@
     });
 
     test('message is not retrieved from storage when other edits', done => {
-      const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
-      const loadSpy = sandbox.spy(element, '_loadLocalDraft');
+      const storageStub = sinon.stub(element.$.storage, 'getDraftComment');
+      const loadSpy = sinon.spy(element, '_loadLocalDraft');
 
       element.changeNum = 1;
       element.patchNum = 1;
@@ -130,8 +129,8 @@
     });
 
     test('message is retrieved from storage when no other edits', done => {
-      const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
-      const loadSpy = sandbox.spy(element, '_loadLocalDraft');
+      const storageStub = sinon.stub(element.$.storage, 'getDraftComment');
+      const loadSpy = sinon.spy(element, '_loadLocalDraft');
 
       element.changeNum = 1;
       element.patchNum = 1;
@@ -188,8 +187,8 @@
       setup(() => {
         element.editing = true;
         element._messageText = 'test';
-        sandbox.stub(element, '_handleCancel');
-        sandbox.stub(element, '_handleSave');
+        sinon.stub(element, '_handleCancel');
+        sinon.stub(element, '_handleSave');
         flushAsynchronousOperations();
       });
 
@@ -265,9 +264,9 @@
     });
 
     test('delete comment', done => {
-      sandbox.stub(
+      sinon.stub(
           element.$.restAPI, 'deleteComment').returns(Promise.resolve({}));
-      sandbox.spy(element.confirmDeleteOverlay, 'open');
+      sinon.spy(element.confirmDeleteOverlay, 'open');
       element.changeNum = 42;
       element.patchNum = 0xDEADBEEF;
       element._isAdmin = true;
@@ -297,12 +296,12 @@
 
       setup(() => {
         mockEvent = {preventDefault() {}};
-        sandbox.stub(element, 'save')
+        sinon.stub(element, 'save')
             .returns(Promise.resolve({}));
-        sandbox.stub(element, '_discardDraft')
+        sinon.stub(element, '_discardDraft')
             .returns(Promise.resolve({}));
         endStub = sinon.stub();
-        getTimerStub = sandbox.stub(element.reporting, 'getTimer')
+        getTimerStub = sinon.stub(element.reporting, 'getTimer')
             .returns({end: endStub});
       });
 
@@ -326,7 +325,7 @@
 
       test('discard', () => {
         element.comment = {id: 'abc_123'};
-        sandbox.stub(element, '_closeConfirmDiscardOverlay');
+        sinon.stub(element, '_closeConfirmDiscardOverlay');
         return element._handleConfirmDiscard(mockEvent).then(() => {
           assert.isTrue(endStub.calledOnce);
           assert.isTrue(getTimerStub.calledOnce);
@@ -336,7 +335,7 @@
     });
 
     test('edit reports interaction', () => {
-      const reportStub = sandbox.stub(element.reporting,
+      const reportStub = sinon.stub(element.reporting,
           'recordDraftInteraction');
       element.draft = true;
       flushAsynchronousOperations();
@@ -346,7 +345,7 @@
     });
 
     test('discard reports interaction', () => {
-      const reportStub = sandbox.stub(element.reporting,
+      const reportStub = sinon.stub(element.reporting,
           'recordDraftInteraction');
       element.draft = true;
       flushAsynchronousOperations();
@@ -358,7 +357,6 @@
 
   suite('gr-comment draft tests', () => {
     let element;
-    let sandbox;
 
     setup(() => {
       stub('gr-rest-api-interface', {
@@ -399,11 +397,6 @@
         line: 5,
       };
       element.commentSide = 'right';
-      sandbox = sinon.sandbox.create();
-    });
-
-    teardown(() => {
-      sandbox.restore();
     });
 
     test('button visibility states', () => {
@@ -658,7 +651,7 @@
       assert.isTrue(element.editing);
 
       element._messageText = '';
-      const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
+      const eraseMessageDraftSpy = sinon.spy(element, '_eraseDraftComment');
 
       // Save should be disabled on an empty message.
       let disabled = element.shadowRoot
@@ -691,8 +684,8 @@
 
     test('draft discard removes message from storage', done => {
       element._messageText = '';
-      const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
-      sandbox.stub(element, '_closeConfirmDiscardOverlay');
+      const eraseMessageDraftSpy = sinon.spy(element, '_eraseDraftComment');
+      sinon.stub(element, '_closeConfirmDiscardOverlay');
 
       element.addEventListener('comment-discard', e => {
         assert.isTrue(eraseMessageDraftSpy.called);
@@ -703,11 +696,11 @@
 
     test('storage is cleared only after save success', () => {
       element._messageText = 'test';
-      const eraseStub = sandbox.stub(element, '_eraseDraftComment');
-      sandbox.stub(element.$.restAPI, 'getResponseObject')
+      const eraseStub = sinon.stub(element, '_eraseDraftComment');
+      sinon.stub(element.$.restAPI, 'getResponseObject')
           .returns(Promise.resolve({}));
 
-      sandbox.stub(element, '_saveDraft').returns(Promise.resolve({ok: false}));
+      sinon.stub(element, '_saveDraft').returns(Promise.resolve({ok: false}));
 
       const savePromise = element.save();
       assert.isFalse(eraseStub.called);
@@ -715,7 +708,7 @@
         assert.isFalse(eraseStub.called);
 
         element._saveDraft.restore();
-        sandbox.stub(element, '_saveDraft')
+        sinon.stub(element, '_saveDraft')
             .returns(Promise.resolve({ok: true}));
         return element.save().then(() => {
           assert.isTrue(eraseStub.called);
@@ -744,8 +737,8 @@
       let mockEvent;
 
       setup(() => {
-        discardStub = sandbox.stub(element, '_discardDraft');
-        overlayStub = sandbox.stub(element, '_openOverlay')
+        discardStub = sinon.stub(element, '_discardDraft');
+        overlayStub = sinon.stub(element, '_openOverlay')
             .returns(Promise.resolve());
         mockEvent = {preventDefault: sinon.stub()};
       });
@@ -766,7 +759,7 @@
     });
 
     test('ctrl+s saves comment', done => {
-      const stub = sinon.stub(element, 'save', () => {
+      const stub = sinon.stub(element, 'save').callsFake(() => {
         assert.isTrue(stub.called);
         stub.restore();
         done();
@@ -782,7 +775,7 @@
 
     test('draft saving/editing', done => {
       const dispatchEventStub = sinon.stub(element, 'dispatchEvent');
-      const cancelDebounce = sandbox.stub(element, 'cancelDebouncer');
+      const cancelDebounce = sinon.stub(element, 'cancelDebouncer');
 
       element.draft = true;
       flushAsynchronousOperations();
@@ -848,7 +841,7 @@
     });
 
     test('draft prevent save when disabled', () => {
-      const saveStub = sandbox.stub(element, 'save').returns(Promise.resolve());
+      const saveStub = sinon.stub(element, 'save').returns(Promise.resolve());
       element.showActions = true;
       element.draft = true;
       flushAsynchronousOperations();
@@ -871,7 +864,7 @@
     });
 
     test('proper event fires on resolve, comment is not saved', done => {
-      const save = sandbox.stub(element, 'save');
+      const save = sinon.stub(element, 'save');
       element.addEventListener('comment-update', e => {
         assert.isTrue(e.detail.comment.unresolved);
         assert.isFalse(save.called);
@@ -882,7 +875,7 @@
     });
 
     test('resolved comment state indicated by checkbox', () => {
-      sandbox.stub(element, 'save');
+      sinon.stub(element, 'save');
       element.comment = {unresolved: false};
       assert.isTrue(element.shadowRoot
           .querySelector('.resolve input').checked);
@@ -893,7 +886,7 @@
 
     test('resolved checkbox saves with tap when !editing', () => {
       element.editing = false;
-      const save = sandbox.stub(element, 'save');
+      const save = sinon.stub(element, 'save');
 
       element.comment = {unresolved: false};
       assert.isTrue(element.shadowRoot
@@ -917,7 +910,7 @@
       });
 
       test('_show{Start,End}Request', () => {
-        const updateStub = sandbox.stub(element, '_updateRequestToast');
+        const updateStub = sinon.stub(element, '_updateRequestToast');
         element._numPendingDraftRequests.number = 1;
 
         element._showStartRequest();
@@ -938,9 +931,9 @@
     });
 
     test('cancelling an unsaved draft discards, persists in storage', () => {
-      const discardSpy = sandbox.spy(element, '_fireDiscard');
-      const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
-      const eraseStub = sandbox.stub(element.$.storage, 'eraseDraftComment');
+      const discardSpy = sinon.spy(element, '_fireDiscard');
+      const storeStub = sinon.stub(element.$.storage, 'setDraftComment');
+      const eraseStub = sinon.stub(element.$.storage, 'eraseDraftComment');
       element._messageText = 'test text';
       flushAsynchronousOperations();
       element.flushDebouncer('store');
@@ -954,8 +947,8 @@
 
     test('cancelling edit on a saved draft does not store', () => {
       element.comment.id = 'foo';
-      const discardSpy = sandbox.spy(element, '_fireDiscard');
-      const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
+      const discardSpy = sinon.spy(element, '_fireDiscard');
+      const storeStub = sinon.stub(element.$.storage, 'setDraftComment');
       element._messageText = 'test text';
       flushAsynchronousOperations();
       element.flushDebouncer('store');
@@ -968,7 +961,7 @@
     test('deleting text from saved draft and saving deletes the draft', () => {
       element.comment = {id: 'foo', message: 'test'};
       element._messageText = '';
-      const discardStub = sandbox.stub(element, '_discardDraft');
+      const discardStub = sinon.stub(element, '_discardDraft');
 
       element.save();
       assert.isTrue(discardStub.called);
@@ -1140,19 +1133,18 @@
 
   suite('respectful tips', () => {
     let element;
-    let sandbox;
+
     let clock;
     setup(() => {
       stub('gr-rest-api-interface', {
         getAccount() { return Promise.resolve(null); },
       });
       clock = sinon.useFakeTimers();
-      sandbox = sinon.sandbox.create();
     });
 
     teardown(() => {
       clock.restore();
-      sandbox.restore();
+      sinon.restore();
     });
 
     test('show tip when no cached record', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js
index 78be439..58c00da 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.js
@@ -23,10 +23,8 @@
 
 suite('gr-copy-clipboard tests', () => {
   let element;
-  let sandbox;
 
   setup(done => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.text = `git fetch http://gerrit@localhost:8080/a/test-project
         refs/changes/05/5/1 && git checkout FETCH_HEAD`;
@@ -34,12 +32,8 @@
     flush(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('copy to clipboard', () => {
-    const clipboardSpy = sandbox.spy(element, '_copyToClipboard');
+    const clipboardSpy = sinon.spy(element, '_copyToClipboard');
     const copyBtn = element.shadowRoot
         .querySelector('.copyToClipboard');
     MockInteractions.tap(copyBtn);
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.js
index 561746a..bc07d84 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.js
@@ -30,21 +30,15 @@
 `);
 
 suite('gr-cursor-manager tests', () => {
-  let sandbox;
   let element;
   let list;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     const fixtureElements = basicTestFixutre.instantiate();
     element = fixtureElements[0];
     list = fixtureElements[1];
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('core cursor functionality', () => {
     // The element is initialized into the proper state.
     assert.isArray(element.stops);
@@ -164,8 +158,8 @@
   });
 
   test('opt_noScroll', () => {
-    sandbox.stub(element, '_targetIsVisible', () => false);
-    const scrollStub = sandbox.stub(window, 'scrollTo');
+    sinon.stub(element, '_targetIsVisible').callsFake(() => false);
+    const scrollStub = sinon.stub(window, 'scrollTo');
     element.stops = list.querySelectorAll('li');
     element.scrollMode = 'keep-visible';
 
@@ -207,7 +201,7 @@
   test('focusOnMove prop', () => {
     const listEls = list.querySelectorAll('li');
     for (let i = 0; i < listEls.length; i++) {
-      sandbox.spy(listEls[i], 'focus');
+      sinon.spy(listEls[i], 'focus');
     }
     element.stops = listEls;
     element.setCursor(list.children[0]);
@@ -230,32 +224,32 @@
       // There is a target which has a targetNext
       element.setCursor(list.children[0]);
       element._moveCursor(1);
-      scrollStub = sandbox.stub(window, 'scrollTo');
+      scrollStub = sinon.stub(window, 'scrollTo');
       window.innerHeight = 60;
     });
 
     test('Called when top and bottom not visible', () => {
-      sandbox.stub(element, '_targetIsVisible').returns(false);
+      sinon.stub(element, '_targetIsVisible').returns(false);
       element._scrollToTarget();
       assert.isTrue(scrollStub.called);
     });
 
     test('Not called when top and bottom visible', () => {
-      sandbox.stub(element, '_targetIsVisible').returns(true);
+      sinon.stub(element, '_targetIsVisible').returns(true);
       element._scrollToTarget();
       assert.isFalse(scrollStub.called);
     });
 
     test('Called when top is visible, bottom is not, scroll is lower', () => {
-      const visibleStub = sandbox.stub(element, '_targetIsVisible',
+      const visibleStub = sinon.stub(element, '_targetIsVisible').callsFake(
           () => visibleStub.callCount === 2);
-      sandbox.stub(element, '_getWindowDims').returns({
+      sinon.stub(element, '_getWindowDims').returns({
         scrollX: 123,
         scrollY: 15,
         innerHeight: 1000,
         pageYOffset: 0,
       });
-      sandbox.stub(element, '_calculateScrollToValue').returns(20);
+      sinon.stub(element, '_calculateScrollToValue').returns(20);
       element._scrollToTarget();
       assert.isTrue(scrollStub.called);
       assert.isTrue(scrollStub.calledWithExactly(123, 20));
@@ -263,22 +257,22 @@
     });
 
     test('Called when top is visible, bottom not, scroll is higher', () => {
-      const visibleStub = sandbox.stub(element, '_targetIsVisible',
+      const visibleStub = sinon.stub(element, '_targetIsVisible').callsFake(
           () => visibleStub.callCount === 2);
-      sandbox.stub(element, '_getWindowDims').returns({
+      sinon.stub(element, '_getWindowDims').returns({
         scrollX: 123,
         scrollY: 25,
         innerHeight: 1000,
         pageYOffset: 0,
       });
-      sandbox.stub(element, '_calculateScrollToValue').returns(20);
+      sinon.stub(element, '_calculateScrollToValue').returns(20);
       element._scrollToTarget();
       assert.isFalse(scrollStub.called);
       assert.equal(visibleStub.callCount, 2);
     });
 
     test('_calculateScrollToValue', () => {
-      sandbox.stub(element, '_getWindowDims').returns({
+      sinon.stub(element, '_getWindowDims').returns({
         scrollX: 123,
         scrollY: 25,
         innerHeight: 300,
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js
index 3ee3665..804c3b7 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js
@@ -26,14 +26,9 @@
 
 suite('gr-date-formatter tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
 
-  teardown(() => {
-    sandbox.restore();
   });
 
   /**
@@ -52,7 +47,7 @@
         .toJSON()
         .replace('T', ' ')
         .slice(0, -1);
-    sandbox.useFakeTimers(normalizedDate(nowStr).getTime());
+    sinon.useFakeTimers(normalizedDate(nowStr).getTime());
     element.dateStr = dateStr;
     flush(() => {
       const span = element.shadowRoot
@@ -83,7 +78,7 @@
       relative_date_in_change_table: false,
     }).then(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element, '_getUtcOffsetString').returns('');
+      sinon.stub(element, '_getUtcOffsetString').returns('');
       return element._loadPreferences();
     }));
 
@@ -132,7 +127,7 @@
       relative_date_in_change_table: false,
     }).then(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element, '_getUtcOffsetString').returns('');
+      sinon.stub(element, '_getUtcOffsetString').returns('');
       return element._loadPreferences();
     }));
 
@@ -168,7 +163,7 @@
       relative_date_in_change_table: false,
     }).then(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element, '_getUtcOffsetString').returns('');
+      sinon.stub(element, '_getUtcOffsetString').returns('');
       return element._loadPreferences();
     }));
 
@@ -204,7 +199,7 @@
       relative_date_in_change_table: false,
     }).then(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element, '_getUtcOffsetString').returns('');
+      sinon.stub(element, '_getUtcOffsetString').returns('');
       return element._loadPreferences();
     }));
 
@@ -240,7 +235,7 @@
       relative_date_in_change_table: false,
     }).then(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element, '_getUtcOffsetString').returns('');
+      sinon.stub(element, '_getUtcOffsetString').returns('');
       return element._loadPreferences();
     }));
 
@@ -276,7 +271,7 @@
           {time_format: 'HHMM_12', date_format: 'STD'}
       ).then(() => {
         element = basicFixture.instantiate();
-        sandbox.stub(element, '_getUtcOffsetString').returns('');
+        sinon.stub(element, '_getUtcOffsetString').returns('');
         return element._loadPreferences();
       })
     );
@@ -297,7 +292,7 @@
           {time_format: 'HHMM_12', date_format: 'US'}
       ).then(() => {
         element = basicFixture.instantiate();
-        sandbox.stub(element, '_getUtcOffsetString').returns('');
+        sinon.stub(element, '_getUtcOffsetString').returns('');
         return element._loadPreferences();
       })
     );
@@ -318,7 +313,7 @@
           {time_format: 'HHMM_12', date_format: 'ISO'}
       ).then(() => {
         element = basicFixture.instantiate();
-        sandbox.stub(element, '_getUtcOffsetString').returns('');
+        sinon.stub(element, '_getUtcOffsetString').returns('');
         return element._loadPreferences();
       })
     );
@@ -339,7 +334,7 @@
           {time_format: 'HHMM_12', date_format: 'EURO'}
       ).then(() => {
         element = basicFixture.instantiate();
-        sandbox.stub(element, '_getUtcOffsetString').returns('');
+        sinon.stub(element, '_getUtcOffsetString').returns('');
         return element._loadPreferences();
       })
     );
@@ -360,7 +355,7 @@
           {time_format: 'HHMM_12', date_format: 'UK'}
       ).then(() => {
         element = basicFixture.instantiate();
-        sandbox.stub(element, '_getUtcOffsetString').returns('');
+        sinon.stub(element, '_getUtcOffsetString').returns('');
         return element._loadPreferences();
       })
     );
@@ -381,7 +376,7 @@
       relative_date_in_change_table: true,
     }).then(() => {
       element = basicFixture.instantiate();
-      sandbox.stub(element, '_getUtcOffsetString').returns('');
+      sinon.stub(element, '_getUtcOffsetString').returns('');
       return element._loadPreferences();
     }));
 
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.js b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.js
index 18f7690..ce36d7b 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.js
@@ -23,15 +23,11 @@
 
 suite('gr-dialog tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('events', done => {
     let numEvents = 0;
     function handler() { if (++numEvents == 2) { done(); } }
@@ -47,8 +43,8 @@
 
   test('confirmOnEnter', () => {
     element.confirmOnEnter = false;
-    const handleConfirmStub = sandbox.stub(element, '_handleConfirm');
-    const handleKeydownSpy = sandbox.spy(element, '_handleKeydown');
+    const handleConfirmStub = sinon.stub(element, '_handleConfirm');
+    const handleKeydownSpy = sinon.spy(element, '_handleKeydown');
     MockInteractions.pressAndReleaseKeyOn(element.shadowRoot
         .querySelector('main'),
     13, null, 'enter');
@@ -67,7 +63,7 @@
   });
 
   test('resetFocus', () => {
-    const focusStub = sandbox.stub(element.$.confirm, 'focus');
+    const focusStub = sinon.stub(element.$.confirm, 'focus');
     element.resetFocus();
     assert.isTrue(focusStub.calledOnce);
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.js
index 4dde2097..ced8c6c 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.js
@@ -22,7 +22,7 @@
 
 suite('gr-diff-preferences tests', () => {
   let element;
-  let sandbox;
+
   let diffPreferences;
 
   function valueOf(title, fieldsetid) {
@@ -57,12 +57,10 @@
     });
 
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
+
     return element.loadData();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('renders', () => {
     // Rendered with the expected preferences selected.
     assert.equal(valueOf('Context', 'diffPreferences')
@@ -91,7 +89,7 @@
   });
 
   test('save changes', () => {
-    sandbox.stub(element.$.restAPI, 'saveDiffPreferences')
+    sinon.stub(element.$.restAPI, 'saveDiffPreferences')
         .returns(Promise.resolve());
     const showTrailingWhitespaceCheckbox =
         valueOf('Show trailing whitespace', 'diffPreferences')
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.js
index 181e00b..0f8b97d 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.js
@@ -23,7 +23,7 @@
 
 suite('gr-download-commands', () => {
   let element;
-  let sandbox;
+
   const SCHEMES = ['http', 'repo', 'ssh'];
   const COMMANDS = [{
     title: 'Checkout',
@@ -45,11 +45,7 @@
   const SELECTED_SCHEME = 'http';
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
 
-  teardown(() => {
-    sandbox.restore();
   });
 
   suite('unauthenticated', () => {
@@ -63,7 +59,7 @@
     });
 
     test('focusOnCopy', () => {
-      const focusStub = sandbox.stub(element.shadowRoot
+      const focusStub = sinon.stub(element.shadowRoot
           .querySelector('gr-shell-command'),
       'focusOnCopy');
       element.focusOnCopy();
@@ -122,8 +118,8 @@
 
     test('saves scheme to preferences', () => {
       element._loggedIn = true;
-      const savePrefsStub = sandbox.stub(element.$.restAPI, 'savePreferences',
-          () => Promise.resolve());
+      const savePrefsStub = sinon.stub(element.$.restAPI, 'savePreferences')
+          .callsFake(() => Promise.resolve());
 
       flushAsynchronousOperations();
 
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.js
index 6e9b57f..8d7de0e 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.js
@@ -23,22 +23,17 @@
 
 suite('gr-dropdown-list tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
     });
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('tap on trigger opens menu', () => {
-    sandbox.stub(element, '_open', () => { element.$.dropdown.open(); });
+    sinon.stub(element, '_open')
+        .callsFake(() => { element.$.dropdown.open(); });
     assert.isFalse(element.$.dropdown.opened);
     MockInteractions.tap(element.$.trigger);
     assert.isTrue(element.$.dropdown.opened);
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.js
index 8f23bb5..d1d9164 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.js
@@ -23,18 +23,12 @@
 
 suite('gr-dropdown tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
     });
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('_computeIsDownload', () => {
@@ -43,8 +37,10 @@
   });
 
   test('tap on trigger opens menu, then closes', () => {
-    sandbox.stub(element, '_open', () => { element.$.dropdown.open(); });
-    sandbox.stub(element, '_close', () => { element.$.dropdown.close(); });
+    sinon.stub(element, '_open')
+        .callsFake(() => { element.$.dropdown.open(); });
+    sinon.stub(element, '_close')
+        .callsFake(() => { element.$.dropdown.close(); });
     assert.isFalse(element.$.dropdown.opened);
     MockInteractions.tap(element.$.trigger);
     assert.isTrue(element.$.dropdown.opened);
@@ -108,8 +104,8 @@
   test('non link items', () => {
     const item0 = {name: 'item one', id: 'foo'};
     element.items = [item0, {name: 'item two', id: 'bar'}];
-    const fooTapped = sandbox.stub();
-    const tapped = sandbox.stub();
+    const fooTapped = sinon.stub();
+    const tapped = sinon.stub();
     element.addEventListener('tap-item-foo', fooTapped);
     element.addEventListener('tap-item', tapped);
     flushAsynchronousOperations();
@@ -124,8 +120,8 @@
     element.items = [{name: 'item one', id: 'foo'}];
     element.disabledIds = ['foo'];
 
-    const stub = sandbox.stub();
-    const tapped = sandbox.stub();
+    const stub = sinon.stub();
+    const tapped = sinon.stub();
     element.addEventListener('tap-item-foo', stub);
     element.addEventListener('tap-item', tapped);
     flushAsynchronousOperations();
@@ -160,7 +156,7 @@
     });
 
     test('down', () => {
-      const stub = sandbox.stub(element.$.cursor, 'next');
+      const stub = sinon.stub(element.$.cursor, 'next');
       assert.isFalse(element.$.dropdown.opened);
       MockInteractions.pressAndReleaseKeyOn(element, 40);
       assert.isTrue(element.$.dropdown.opened);
@@ -169,7 +165,7 @@
     });
 
     test('up', () => {
-      const stub = sandbox.stub(element.$.cursor, 'previous');
+      const stub = sinon.stub(element.$.cursor, 'previous');
       assert.isFalse(element.$.dropdown.opened);
       MockInteractions.pressAndReleaseKeyOn(element, 38);
       assert.isTrue(element.$.dropdown.opened);
@@ -185,7 +181,7 @@
       assert.isTrue(element.$.dropdown.opened);
 
       const el = element.$.cursor.target.querySelector(':not([hidden]) a');
-      const stub = sandbox.stub(el, 'click');
+      const stub = sinon.stub(el, 'click');
       MockInteractions.pressAndReleaseKeyOn(element, 32); // Space
       assert.isTrue(stub.called);
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.js b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.js
index 82c4eff..0a9dd79 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.js
@@ -22,15 +22,11 @@
 
 suite('gr-editable-content tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('save event', done => {
     element.content = '';
     element._newContent = 'foo';
@@ -95,11 +91,11 @@
     setup(() => {
       element.content = 'current content';
       element.storageKey = 'test';
-      dispatchSpy = sandbox.spy(element, 'dispatchEvent');
+      dispatchSpy = sinon.spy(element, 'dispatchEvent');
     });
 
     test('editing toggled to true, has stored data', () => {
-      sandbox.stub(element.$.storage, 'getEditableContentItem')
+      sinon.stub(element.$.storage, 'getEditableContentItem')
           .returns({message: 'stored content'});
       element.editing = true;
 
@@ -109,7 +105,7 @@
     });
 
     test('editing toggled to true, has no stored data', () => {
-      sandbox.stub(element.$.storage, 'getEditableContentItem')
+      sinon.stub(element.$.storage, 'getEditableContentItem')
           .returns({});
       element.editing = true;
 
@@ -119,9 +115,9 @@
 
     test('edits are cached', () => {
       const storeStub =
-          sandbox.stub(element.$.storage, 'setEditableContentItem');
+          sinon.stub(element.$.storage, 'setEditableContentItem');
       const eraseStub =
-          sandbox.stub(element.$.storage, 'eraseEditableContentItem');
+          sinon.stub(element.$.storage, 'eraseEditableContentItem');
       element.editing = true;
 
       element._newContent = 'new content';
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.js
index 6a4ccb0..8c04aed 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.js
@@ -42,7 +42,6 @@
   let elementNoPlaceholder;
   let input;
   let label;
-  let sandbox;
 
   setup(done => {
     element = basicFixture.instantiate();
@@ -50,7 +49,7 @@
 
     label = element.shadowRoot
         .querySelector('label');
-    sandbox = sinon.sandbox.create();
+
     flush(() => {
       // In Polymer 2 inputElement isn't nativeInput anymore
       input = element.$.input.$.nativeInput || element.$.input.inputElement;
@@ -58,17 +57,13 @@
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('element render', () => {
     // The dropdown is closed and the label is visible:
     assert.isFalse(element.$.dropdown.opened);
     assert.isTrue(label.classList.contains('editable'));
     assert.equal(label.textContent, 'value text');
-    const focusSpy = sandbox.spy(input, 'focus');
-    const showSpy = sandbox.spy(element, '_showDropdown');
+    const focusSpy = sinon.spy(input, 'focus');
+    const showSpy = sinon.spy(element, '_showDropdown');
 
     MockInteractions.tap(label);
 
@@ -101,7 +96,7 @@
   });
 
   test('edit value', done => {
-    const editedStub = sandbox.stub();
+    const editedStub = sinon.stub();
     element.addEventListener('changed', editedStub);
     assert.isFalse(element.editing);
 
@@ -126,7 +121,7 @@
   });
 
   test('save button', done => {
-    const editedStub = sandbox.stub();
+    const editedStub = sinon.stub();
     element.addEventListener('changed', editedStub);
     assert.isFalse(element.editing);
 
@@ -151,7 +146,7 @@
   });
 
   test('edit and then escape key', done => {
-    const editedStub = sandbox.stub();
+    const editedStub = sinon.stub();
     element.addEventListener('changed', editedStub);
     assert.isFalse(element.editing);
 
@@ -177,7 +172,7 @@
   });
 
   test('cancel button', done => {
-    const editedStub = sandbox.stub();
+    const editedStub = sinon.stub();
     element.addEventListener('changed', editedStub);
     assert.isFalse(element.editing);
 
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.js
index 13cbd75..b9378ba 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.js
@@ -27,21 +27,15 @@
 
 suite('gr-fixed-panel', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.readyForMeasure = true;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('can be disabled with floatingDisabled', () => {
     element.floatingDisabled = true;
-    sandbox.stub(element, '_reposition');
+    sinon.stub(element, '_reposition');
     window.dispatchEvent(new CustomEvent('resize'));
     element.flushDebouncer('update');
     assert.isFalse(element._reposition.called);
@@ -52,7 +46,7 @@
   });
 
   test('scroll triggers _reposition', () => {
-    sandbox.stub(element, '_reposition');
+    sinon.stub(element, '_reposition');
     window.dispatchEvent(new CustomEvent('scroll'));
     element.flushDebouncer('update');
     assert.isTrue(element._reposition.called);
@@ -71,7 +65,7 @@
 
     setup(() => {
       element._headerTopInitial = 10;
-      sandbox.stub(element, '_getElementTop')
+      sinon.stub(element, '_getElementTop')
           .returns(element._headerTopInitial);
     });
 
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.js
index 3b7f716..fd5a9ba 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.js
@@ -22,7 +22,6 @@
 
 suite('gr-formatted-text tests', () => {
   let element;
-  let sandbox;
 
   function assertBlock(result, index, type, text) {
     assert.equal(result[index].type, type);
@@ -36,11 +35,6 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('parse null undefined and empty', () => {
@@ -395,14 +389,14 @@
   });
 
   test('_computeNodes called without config', () => {
-    const computeNodesSpy = sandbox.spy(element, '_computeNodes');
+    const computeNodesSpy = sinon.spy(element, '_computeNodes');
     element.content = 'some text';
     assert.isTrue(computeNodesSpy.called);
   });
 
   test('_contentOrConfigChanged called with config', () => {
-    const contentStub = sandbox.stub(element, '_contentChanged');
-    const contentConfigStub = sandbox.stub(element, '_contentOrConfigChanged');
+    const contentStub = sinon.stub(element, '_contentChanged');
+    const contentConfigStub = sinon.stub(element, '_contentOrConfigChanged');
     element.content = 'some text';
     element.config = {};
     assert.isTrue(contentStub.called);
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
index 1b7bcec..ea7eb87 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
@@ -25,7 +25,7 @@
 
 suite('gr-hovercard-account tests', () => {
   let element;
-  let sandbox;
+
   const ACCOUNT = {
     email: 'kermit@gmail.com',
     username: 'kermit',
@@ -34,9 +34,8 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
-    sandbox.stub(element.$.restAPI, 'getAccount').returns(
+    sinon.stub(element.$.restAPI, 'getAccount').returns(
         new Promise(resolve => { '2'; })
     );
 
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.js
index 0d5a3f0..a7a2c0e 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.js
@@ -28,12 +28,10 @@
 
 suite('gr-hovercard tests', () => {
   let element;
-  let sandbox;
+
   let button;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     button = document.createElement('button');
     button.innerHTML = 'Hello';
     button.setAttribute('id', 'foo');
@@ -43,7 +41,6 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     element.hide({});
     button.remove();
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.js
index 4f931f9..b46a3b0 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.js
@@ -25,13 +25,12 @@
 
 suite('gr-annotation-actions-context tests', () => {
   let instance;
-  let sandbox;
+
   let el;
   let lineNumberEl;
   let plugin;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
 
@@ -48,12 +47,11 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     el.remove();
   });
 
   test('test annotateRange', () => {
-    const annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
+    const annotateElementSpy = sinon.spy(GrAnnotation, 'annotateElement');
     const start = 0;
     const end = 100;
     const cssStyleObject = plugin.styles().css('background-color: #000000');
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js
index 4bbe7e5..e819529 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js
@@ -33,11 +33,10 @@
 
 suite('gr-annotation-actions-js-api tests', () => {
   let annotationActions;
-  let sandbox;
+
   let plugin;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
     annotationActions = plugin.annotationApi();
@@ -45,7 +44,6 @@
 
   teardown(() => {
     annotationActions = null;
-    sandbox.restore();
   });
 
   test('add/get layer', () => {
@@ -78,8 +76,8 @@
     const path2 = '/dummy/path2';
     const annotationLayer1 = annotationActions.getLayer(path1, 1, 2);
     const annotationLayer2 = annotationActions.getLayer(path2, 1, 2);
-    const layer1Spy = sandbox.spy(annotationLayer1, 'notifyListeners');
-    const layer2Spy = sandbox.spy(annotationLayer2, 'notifyListeners');
+    const layer1Spy = sinon.spy(annotationLayer1, 'notifyListeners');
+    const layer2Spy = sinon.spy(annotationLayer2, 'notifyListeners');
 
     let notify;
     let notifyFuncCalled;
@@ -101,8 +99,8 @@
     assert.isFalse(layer2Spy.called);
 
     // Reset spies.
-    layer1Spy.reset();
-    layer2Spy.reset();
+    layer1Spy.resetHistory();
+    layer2Spy.resetHistory();
 
     // Assert that only the 2nd layer is invoked with path2.
     notify(path2, 0, 20, 'left');
@@ -112,8 +110,8 @@
 
   test('toggle checkbox', () => {
     const fakeEl = {content: basicFixture.instantiate()};
-    const hookStub = {onAttached: sandbox.stub()};
-    sandbox.stub(plugin, 'hook').returns(hookStub);
+    const hookStub = {onAttached: sinon.stub()};
+    sinon.stub(plugin, 'hook').returns(hookStub);
 
     let checkbox;
     let onAttachedFuncCalled = false;
@@ -137,8 +135,8 @@
     // Assert that error is shown if we try to enable checkbox again.
     onAttachedFuncCalled = false;
     annotationActions.enableToggleCheckbox('test label2', onAttachedFunc);
-    const errorStub = sandbox.stub(
-        console, 'error', (msg, err) => undefined);
+    const errorStub = sinon.stub(
+        console, 'error').callsFake((msg, err) => undefined);
     emulateAttached();
     assert.isTrue(
         errorStub.calledWith(
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.js
index f545a38..8f41b39 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.js
@@ -25,22 +25,17 @@
 
 suite('gr-change-reply-js-api tests', () => {
   let element;
-  let sandbox;
+
   let changeReply;
   let plugin;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
       getAccount() { return Promise.resolve(null); },
     });
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('early init', () => {
     setup(() => {
       pluginApi.install(p => { plugin = p; }, '0.1',
@@ -54,19 +49,19 @@
     });
 
     test('works', () => {
-      sandbox.stub(element, 'getLabelValue').returns('+123');
+      sinon.stub(element, 'getLabelValue').returns('+123');
       assert.equal(changeReply.getLabelValue('My-Label'), '+123');
 
-      sandbox.stub(element, 'setLabelValue');
+      sinon.stub(element, 'setLabelValue');
       changeReply.setLabelValue('My-Label', '+1337');
       assert.isTrue(
           element.setLabelValue.calledWithExactly('My-Label', '+1337'));
 
-      sandbox.stub(element, 'send');
+      sinon.stub(element, 'send');
       changeReply.send(false);
       assert.isTrue(element.send.calledWithExactly(false));
 
-      sandbox.stub(element, 'setPluginMessage');
+      sinon.stub(element, 'setPluginMessage');
       changeReply.showMessage('foobar');
       assert.isTrue(element.setPluginMessage.calledWithExactly('foobar'));
     });
@@ -85,19 +80,19 @@
     });
 
     test('works', () => {
-      sandbox.stub(element, 'getLabelValue').returns('+123');
+      sinon.stub(element, 'getLabelValue').returns('+123');
       assert.equal(changeReply.getLabelValue('My-Label'), '+123');
 
-      sandbox.stub(element, 'setLabelValue');
+      sinon.stub(element, 'setLabelValue');
       changeReply.setLabelValue('My-Label', '+1337');
       assert.isTrue(
           element.setLabelValue.calledWithExactly('My-Label', '+1337'));
 
-      sandbox.stub(element, 'send');
+      sinon.stub(element, 'send');
       changeReply.send(false);
       assert.isTrue(element.send.calledWithExactly(false));
 
-      sandbox.stub(element, 'setPluginMessage');
+      sinon.stub(element, 'setPluginMessage');
       changeReply.showMessage('foobar');
       assert.isTrue(element.setPluginMessage.calledWithExactly('foobar'));
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.js
index d8f240c..e5b32f7 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.js
@@ -27,13 +27,13 @@
 
 suite('gr-gerrit tests', () => {
   let element;
-  let sandbox;
+
   let sendStub;
 
   setup(() => {
     window.clock = sinon.useFakeTimers();
-    sandbox = sinon.sandbox.create();
-    sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+
+    sendStub = sinon.stub().returns(Promise.resolve({status: 200}));
     stub('gr-rest-api-interface', {
       getAccount() {
         return Promise.resolve({name: 'Judy Hopps'});
@@ -47,41 +47,38 @@
 
   teardown(() => {
     window.clock.restore();
-    sandbox.restore();
     element._removeEventCallbacks();
     resetPlugins();
   });
 
   suite('proxy methods', () => {
     test('Gerrit._isPluginEnabled proxy to pluginLoader', () => {
-      const stubFn = sandbox.stub();
-      sandbox.stub(
+      const stubFn = sinon.stub();
+      sinon.stub(
           pluginLoader,
-          'isPluginEnabled',
-          (...args) => stubFn(...args)
-      );
+          'isPluginEnabled')
+          .callsFake((...args) => stubFn(...args)
+          );
       pluginApi._isPluginEnabled('test_plugin');
       assert.isTrue(stubFn.calledWith('test_plugin'));
     });
 
     test('Gerrit._isPluginLoaded proxy to pluginLoader', () => {
-      const stubFn = sandbox.stub();
-      sandbox.stub(
+      const stubFn = sinon.stub();
+      sinon.stub(
           pluginLoader,
-          'isPluginLoaded',
-          (...args) => stubFn(...args)
-      );
+          'isPluginLoaded')
+          .callsFake((...args) => stubFn(...args));
       pluginApi._isPluginLoaded('test_plugin');
       assert.isTrue(stubFn.calledWith('test_plugin'));
     });
 
     test('Gerrit._isPluginPreloaded proxy to pluginLoader', () => {
-      const stubFn = sandbox.stub();
-      sandbox.stub(
+      const stubFn = sinon.stub();
+      sinon.stub(
           pluginLoader,
-          'isPluginPreloaded',
-          (...args) => stubFn(...args)
-      );
+          'isPluginPreloaded')
+          .callsFake((...args) => stubFn(...args));
       pluginApi._isPluginPreloaded('test_plugin');
       assert.isTrue(stubFn.calledWith('test_plugin'));
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js
index 0c41180..69d635b 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js
@@ -33,7 +33,7 @@
   let element;
   let plugin;
   let errorStub;
-  let sandbox;
+
   let getResponseObjectStub;
   let sendStub;
 
@@ -43,9 +43,9 @@
 
   setup(() => {
     window.clock = sinon.useFakeTimers();
-    sandbox = sinon.sandbox.create();
-    getResponseObjectStub = sandbox.stub().returns(Promise.resolve());
-    sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+
+    getResponseObjectStub = sinon.stub().returns(Promise.resolve());
+    sendStub = sinon.stub().returns(Promise.resolve({status: 200}));
     stub('gr-rest-api-interface', {
       getAccount() {
         return Promise.resolve({name: 'Judy Hopps'});
@@ -56,7 +56,7 @@
       },
     });
     element = basicFixture.instantiate();
-    errorStub = sandbox.stub(console, 'error');
+    errorStub = sinon.stub(console, 'error');
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
     pluginLoader.loadPlugins([]);
@@ -64,7 +64,6 @@
 
   teardown(() => {
     window.clock.restore();
-    sandbox.restore();
     element._removeEventCallbacks();
     plugin = null;
   });
@@ -227,7 +226,7 @@
       _number: 42,
       revisions: {def: {_number: 2}, abc: {_number: 1}},
     };
-    const spy = sandbox.spy();
+    const spy = sinon.spy();
     pluginLoader.loadPlugins(['plugins/test.html']);
     plugin.on(element.EventType.SHOW_CHANGE, spy);
     element.handleEvent(element.EventType.SHOW_CHANGE,
@@ -335,7 +334,7 @@
 
   test('getLoggedIn', done => {
     // fake fetch for authCheck
-    sandbox.stub(window, 'fetch', () => Promise.resolve({status: 204}));
+    sinon.stub(window, 'fetch').callsFake(() => Promise.resolve({status: 204}));
     plugin.restApi().getLoggedIn()
         .then(loggedIn => {
           assert.isTrue(loggedIn);
@@ -356,7 +355,7 @@
 
   test('getAdminMenuLinks', () => {
     const links = [{text: 'a', url: 'b'}, {text: 'c', url: 'd'}];
-    const getCallbacksStub = sandbox.stub(element, '_getEventCallbacks')
+    const getCallbacksStub = sinon.stub(element, '_getEventCallbacks')
         .returns([
           {getMenuLinks: () => [links[0]]},
           {getMenuLinks: () => [links[1]]},
@@ -372,7 +371,7 @@
     let baseUrlPlugin;
 
     setup(() => {
-      sandbox.stub(BaseUrlBehavior, 'getBaseUrl').returns('/r');
+      sinon.stub(BaseUrlBehavior, 'getBaseUrl').returns('/r');
 
       pluginApi.install(p => { baseUrlPlugin = p; }, '0.1',
           'http://test.com/r/plugins/baseurlplugin/static/test.js');
@@ -395,7 +394,7 @@
     });
 
     test('popup(moduleName) creates popup with component', () => {
-      const openStub = sandbox.stub(GrPopupInterface.prototype, 'open',
+      const openStub = sinon.stub(GrPopupInterface.prototype, 'open').callsFake(
           function() {
             // Arrow function can't be used here, because we want to
             // get properties from the instance of GrPopupInterface
@@ -411,7 +410,7 @@
     test('deprecated.popup(element) creates popup with element', () => {
       const el = document.createElement('div');
       el.textContent = 'some text here';
-      const openStub = sandbox.stub(GrPopupInterface.prototype, 'open');
+      const openStub = sinon.stub(GrPopupInterface.prototype, 'open');
       openStub.returns(Promise.resolve({
         _getElement() {
           return document.createElement('div');
@@ -430,15 +429,15 @@
       change = {};
       revision = {};
       actionDetails = {__key: 'some'};
-      sandbox.stub(plugin, 'on').callsArgWith(1, change, revision);
-      sandbox.stub(plugin, 'changeActions').returns({
-        addTapListener: sandbox.stub().callsArg(1),
+      sinon.stub(plugin, 'on').callsArgWith(1, change, revision);
+      sinon.stub(plugin, 'changeActions').returns({
+        addTapListener: sinon.stub().callsArg(1),
         getActionDetails: () => actionDetails,
       });
     });
 
     test('returns GrPluginActionContext', () => {
-      const stub = sandbox.stub();
+      const stub = sinon.stub();
       plugin.deprecated.onAction('change', 'foo', ctx => {
         assert.isTrue(ctx instanceof GrPluginActionContext);
         assert.strictEqual(ctx.change, change);
@@ -451,7 +450,7 @@
     });
 
     test('other actions', () => {
-      const stub = sandbox.stub();
+      const stub = sinon.stub();
       plugin.deprecated.onAction('project', 'foo', stub);
       plugin.deprecated.onAction('edit', 'foo', stub);
       plugin.deprecated.onAction('branch', 'foo', stub);
@@ -461,7 +460,7 @@
 
   suite('screen', () => {
     test('screenUrl()', () => {
-      sandbox.stub(BaseUrlBehavior, 'getBaseUrl').returns('/base');
+      sinon.stub(BaseUrlBehavior, 'getBaseUrl').returns('/base');
       assert.equal(
           plugin.screenUrl(),
           `${location.origin}/base/x/testplugin`
@@ -473,9 +472,9 @@
     });
 
     test('deprecated works', () => {
-      const stub = sandbox.stub();
-      const hookStub = {onAttached: sandbox.stub()};
-      sandbox.stub(plugin, 'hook').returns(hookStub);
+      const stub = sinon.stub();
+      const hookStub = {onAttached: sinon.stub()};
+      sinon.stub(plugin, 'hook').returns(hookStub);
       plugin.deprecated.screen('foo', stub);
       assert.isTrue(plugin.hook.calledWith('testplugin-screen-foo'));
       const fakeEl = {style: {display: ''}};
@@ -485,7 +484,7 @@
     });
 
     test('works', () => {
-      sandbox.stub(plugin, 'registerCustomComponent');
+      sinon.stub(plugin, 'registerCustomComponent');
       plugin.screen('foo', 'some-module');
       assert.isTrue(plugin.registerCustomComponent.calledWith(
           'testplugin-screen-foo', 'some-module'));
@@ -498,8 +497,8 @@
 
     setup(()=> {
       fakeEl = {change: {}, revision: {}};
-      const hookStub = {onAttached: sandbox.stub()};
-      sandbox.stub(plugin, 'hook').returns(hookStub);
+      const hookStub = {onAttached: sinon.stub()};
+      sinon.stub(plugin, 'hook').returns(hookStub);
       emulateAttached = () => hookStub.onAttached.callArgWith(0, fakeEl);
     });
 
@@ -513,7 +512,7 @@
       ['CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK', 'change-metadata-item'],
     ].forEach(([panelName, endpointName]) => {
       test(`deprecated.panel works for ${panelName}`, () => {
-        const callback = sandbox.stub();
+        const callback = sinon.stub();
         plugin.deprecated.panel(panelName, callback);
         assert.isTrue(plugin.hook.calledWith(endpointName));
         emulateAttached();
@@ -538,15 +537,15 @@
     });
 
     test('plugin.deprecated.settingsScreen() works', () => {
-      const hookStub = {onAttached: sandbox.stub()};
-      sandbox.stub(plugin, 'hook').returns(hookStub);
+      const hookStub = {onAttached: sinon.stub()};
+      sinon.stub(plugin, 'hook').returns(hookStub);
       const fakeSettings = {};
-      fakeSettings.title = sandbox.stub().returns(fakeSettings);
-      fakeSettings.token = sandbox.stub().returns(fakeSettings);
-      fakeSettings.module = sandbox.stub().returns(fakeSettings);
-      fakeSettings.build = sandbox.stub().returns(hookStub);
-      sandbox.stub(plugin, 'settings').returns(fakeSettings);
-      const callback = sandbox.stub();
+      fakeSettings.title = sinon.stub().returns(fakeSettings);
+      fakeSettings.token = sinon.stub().returns(fakeSettings);
+      fakeSettings.module = sinon.stub().returns(fakeSettings);
+      fakeSettings.build = sinon.stub().returns(hookStub);
+      sinon.stub(plugin, 'settings').returns(fakeSettings);
+      const callback = sinon.stub();
 
       plugin.deprecated.settingsScreen('path', 'menu', callback);
       assert.isTrue(fakeSettings.title.calledWith('menu'));
@@ -559,7 +558,7 @@
         style: {
           display: '',
         },
-        querySelector: sandbox.stub().returns(fakeBody),
+        querySelector: sinon.stub().returns(fakeBody),
       };
       // Emulate settings screen attached
       hookStub.onAttached.callArgWith(0, fakeEl);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.js
index dda560f..dde3c04 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.js
@@ -25,25 +25,20 @@
 
 suite('gr-plugin-action-context tests', () => {
   let instance;
-  let sandbox;
+
   let plugin;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     pluginApi.install(p => { plugin = p; }, '0.1',
         'http://test.com/plugins/testplugin/static/test.js');
     instance = new GrPluginActionContext(plugin);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('popup() and hide()', () => {
     const popupApiStub = {
-      close: sandbox.stub(),
+      close: sinon.stub(),
     };
-    sandbox.stub(plugin.deprecated, 'popup').returns(popupApiStub);
+    sinon.stub(plugin.deprecated, 'popup').returns(popupApiStub);
     const el = {};
     instance.popup(el);
     assert.isTrue(instance.plugin.deprecated.popup.calledWith(el));
@@ -80,7 +75,7 @@
     let clickStub;
     let button;
     setup(() => {
-      clickStub = sandbox.stub();
+      clickStub = sinon.stub();
       button = instance.button('foo', {onclick: clickStub});
       // If you don't attach a Polymer element to the DOM, then the ready()
       // callback will not be called and then e.g. this.$ is undefined.
@@ -110,8 +105,8 @@
   test('label', () => {
     const fakeMsg = {};
     const fakeCheckbox = {};
-    sandbox.stub(instance, 'div');
-    sandbox.stub(instance, 'msg').returns(fakeMsg);
+    sinon.stub(instance, 'div');
+    sinon.stub(instance, 'msg').returns(fakeMsg);
     instance.label(fakeCheckbox, 'foo');
     assert.isTrue(instance.div.calledWithExactly(fakeCheckbox, fakeMsg));
   });
@@ -122,12 +117,12 @@
       __key: 'key',
       __url: '/changes/1/revisions/2/foo~bar',
     };
-    const sendStub = sandbox.stub().returns(Promise.resolve());
-    sandbox.stub(plugin, 'restApi').returns({
+    const sendStub = sinon.stub().returns(Promise.resolve());
+    sinon.stub(plugin, 'restApi').returns({
       send: sendStub,
     });
     const payload = {foo: 'foo'};
-    const successStub = sandbox.stub();
+    const successStub = sinon.stub();
     instance.call(payload, successStub);
     assert.isTrue(sendStub.calledWith(
         'METHOD', '/changes/1/revisions/2/foo~bar', payload));
@@ -139,11 +134,11 @@
       __key: 'key',
       __url: '/changes/1/revisions/2/foo~bar',
     };
-    const sendStub = sandbox.stub().returns(Promise.reject(new Error('boom')));
-    sandbox.stub(plugin, 'restApi').returns({
+    const sendStub = sinon.stub().returns(Promise.reject(new Error('boom')));
+    sinon.stub(plugin, 'restApi').returns({
       send: sendStub,
     });
-    const errorStub = sandbox.stub();
+    const errorStub = sinon.stub();
     document.addEventListener('show-alert', errorStub);
     instance.call();
     flush(() => {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js
index e6767bc..f1af433 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js
@@ -25,14 +25,12 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-plugin-endpoints tests', () => {
-  let sandbox;
   let instance;
   let pluginFoo;
   let pluginBar;
   let domHook;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     domHook = {};
     instance = new GrPluginEndpoints();
     pluginApi.install(p => { pluginFoo = p; }, '0.1',
@@ -57,12 +55,11 @@
           domHook,
         }
     );
-    sandbox.stub(pluginLoader, 'arePluginsLoaded').returns(true);
-    sandbox.spy(instance, 'importUrl');
+    sinon.stub(pluginLoader, 'arePluginsLoaded').returns(true);
+    sinon.spy(instance, 'importUrl');
   });
 
   teardown(() => {
-    sandbox.restore();
     resetPlugins();
   });
 
@@ -134,7 +131,7 @@
   });
 
   test('onNewEndpoint', () => {
-    const newModuleStub = sandbox.stub();
+    const newModuleStub = sinon.stub();
     instance.onNewEndpoint('a-place', newModuleStub);
     instance.registerModule(
         pluginFoo,
@@ -176,4 +173,4 @@
       },
     ]);
   });
-});
\ No newline at end of file
+});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.js
index 5f0ee0f..e8839d6 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.js
@@ -30,15 +30,15 @@
 
 suite('gr-plugin-loader tests', () => {
   let plugin;
-  let sandbox;
+
   let url;
   let sendStub;
   let pluginLoader;
 
   setup(() => {
     window.clock = sinon.useFakeTimers();
-    sandbox = sinon.sandbox.create();
-    sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+
+    sendStub = sinon.stub().returns(Promise.resolve({status: 200}));
     stub('gr-rest-api-interface', {
       getAccount() {
         return Promise.resolve({name: 'Judy Hopps'});
@@ -48,13 +48,12 @@
       },
     });
     pluginLoader = _testOnly_resetPluginLoader();
-    sandbox.stub(document.body, 'appendChild');
+    sinon.stub(document.body, 'appendChild');
     basicFixture.instantiate();
     url = window.location.origin;
   });
 
   teardown(() => {
-    sandbox.restore();
     window.clock.restore();
     resetPlugins();
   });
@@ -73,20 +72,20 @@
     assert.doesNotThrow(() => {
       _testOnly_flushPreinstalls();
     });
-    window.Gerrit.flushPreinstalls = sandbox.stub();
+    window.Gerrit.flushPreinstalls = sinon.stub();
     _testOnly_flushPreinstalls();
     assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
     delete window.Gerrit.flushPreinstalls;
   });
 
   test('versioning', () => {
-    const callback = sandbox.spy();
+    const callback = sinon.spy();
     pluginApi.install(callback, '0.0pre-alpha');
     assert(callback.notCalled);
   });
 
   test('report pluginsLoaded', done => {
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
     pluginsLoadedStub.reset();
     window.Gerrit._loadPlugins([]);
@@ -115,10 +114,10 @@
   });
 
   test('plugins installed successfully', done => {
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => void 0, undefined, url);
     });
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
 
     const plugins = [
@@ -135,7 +134,7 @@
   });
 
   test('isPluginEnabled and isPluginLoaded', done => {
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => void 0, undefined, url);
     });
 
@@ -165,10 +164,10 @@
       'http://test.com/plugins/bar/static/test.js',
     ];
 
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     document.addEventListener('show-alert', alertStub);
 
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => {
         if (url === plugins[0]) {
           throw new Error('failed');
@@ -176,7 +175,7 @@
       }, undefined, url);
     });
 
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
@@ -195,10 +194,10 @@
       'http://test.com/plugins/bar/static/test.js',
     ];
 
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     document.addEventListener('show-alert', alertStub);
 
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => {
         if (url === plugins[0]) {
           throw new Error('failed');
@@ -206,7 +205,7 @@
       }, undefined, url);
     });
 
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
@@ -230,16 +229,16 @@
       'http://test.com/plugins/bar/static/test.js',
     ];
 
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     document.addEventListener('show-alert', alertStub);
 
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => {
         throw new Error('failed');
       }, undefined, url);
     });
 
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
@@ -258,15 +257,15 @@
       'http://test.com/plugins/bar/static/test.js',
     ];
 
-    const alertStub = sandbox.stub();
+    const alertStub = sinon.stub();
     document.addEventListener('show-alert', alertStub);
 
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => {
       }, url === plugins[0] ? '' : 'alpha', url);
     });
 
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
@@ -280,10 +279,10 @@
   });
 
   test('multiple assets for same plugin installed successfully', done => {
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => void 0, undefined, url);
     });
-    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+    const pluginsLoadedStub = sinon.stub(pluginLoader._getReporting(),
         'pluginsLoaded');
 
     const plugins = [
@@ -304,19 +303,19 @@
     let importHtmlPluginStub;
     let loadJsPluginStub;
     setup(() => {
-      importHtmlPluginStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_loadHtmlPlugin', url => {
+      importHtmlPluginStub = sinon.stub();
+      sinon.stub(pluginLoader, '_loadHtmlPlugin').callsFake( url => {
         importHtmlPluginStub(url);
       });
-      loadJsPluginStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_createScriptTag', url => {
+      loadJsPluginStub = sinon.stub();
+      sinon.stub(pluginLoader, '_createScriptTag').callsFake( url => {
         loadJsPluginStub(url);
       });
     });
 
     test('invalid plugin path', () => {
-      const failToLoadStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_failToLoad', (...args) => {
+      const failToLoadStub = sinon.stub();
+      sinon.stub(pluginLoader, '_failToLoad').callsFake((...args) => {
         failToLoadStub(...args);
       });
 
@@ -349,7 +348,7 @@
 
     test('relative path should honor getBaseUrl', () => {
       const testUrl = '/test';
-      sandbox.stub(BaseUrlBehavior, 'getBaseUrl', () => testUrl);
+      sinon.stub(BaseUrlBehavior, 'getBaseUrl').callsFake(() => testUrl);
 
       pluginLoader.loadPlugins([
         'foo/bar.js',
@@ -390,12 +389,12 @@
     let loadJsPluginStub;
     setup(() => {
       window.ASSETS_PATH = 'https://cdn.com';
-      importHtmlPluginStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_loadHtmlPlugin', url => {
+      importHtmlPluginStub = sinon.stub();
+      sinon.stub(pluginLoader, '_loadHtmlPlugin').callsFake( url => {
         importHtmlPluginStub(url);
       });
-      loadJsPluginStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_createScriptTag', url => {
+      loadJsPluginStub = sinon.stub();
+      sinon.stub(pluginLoader, '_createScriptTag').callsFake( url => {
         loadJsPluginStub(url);
       });
     });
@@ -471,7 +470,7 @@
         installed = true;
       }
     }
-    sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+    sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
       pluginApi.install(() => pluginCallback(url), undefined, url);
     });
 
@@ -491,12 +490,12 @@
       window.Gerrit._preloadedPlugins = null;
     });
     test('skips preloaded plugins when load plugins', () => {
-      const importHtmlPluginStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_importHtmlPlugin', url => {
+      const importHtmlPluginStub = sinon.stub();
+      sinon.stub(pluginLoader, '_importHtmlPlugin').callsFake( url => {
         importHtmlPluginStub(url);
       });
-      const loadJsPluginStub = sandbox.stub();
-      sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
+      const loadJsPluginStub = sinon.stub();
+      sinon.stub(pluginLoader, '_loadJsPlugin').callsFake( url => {
         loadJsPluginStub(url);
       });
 
@@ -525,7 +524,7 @@
     });
 
     test('preloaded plugins are installed', () => {
-      const installStub = sandbox.stub();
+      const installStub = sinon.stub();
       window.Gerrit._preloadedPlugins = {foo: installStub};
       pluginLoader.installPreloadedPlugins();
       assert.isTrue(installStub.called);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.js
index 69c8c44..53aaa1e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.js
@@ -24,22 +24,21 @@
 
 suite('gr-plugin-rest-api tests', () => {
   let instance;
-  let sandbox;
+
   let getResponseObjectStub;
   let sendStub;
   let restApiStub;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-    getResponseObjectStub = sandbox.stub().returns(Promise.resolve());
-    sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+    getResponseObjectStub = sinon.stub().returns(Promise.resolve());
+    sendStub = sinon.stub().returns(Promise.resolve({status: 200}));
     restApiStub = {
       getAccount: () => Promise.resolve({name: 'Judy Hopps'}),
       getResponseObject: getResponseObjectStub,
       send: sendStub,
-      getLoggedIn: sandbox.stub(),
-      getVersion: sandbox.stub(),
-      getConfig: sandbox.stub(),
+      getLoggedIn: sinon.stub(),
+      getVersion: sinon.stub(),
+      getConfig: sinon.stub(),
     };
     stub('gr-rest-api-interface', Object.keys(restApiStub).reduce((a, k) => {
       a[k] = (...args) => restApiStub[k](...args);
@@ -50,10 +49,6 @@
     instance = new GrPluginRestApi();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('fetch', () => {
     const payload = {foo: 'foo'};
     return instance.fetch('HTTP_METHOD', '/url', payload).then(r => {
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.js
index 0f2ea78..ee9fb13 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.js
@@ -24,23 +24,18 @@
 
 suite('gr-account-link tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
+
     // Needed to trigger computed bindings.
     element.account = {};
     element.change = {labels: {}};
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('remove reviewer votes', () => {
     setup(() => {
-      sandbox.stub(element, '_computeValueTooltip').returns('');
+      sinon.stub(element, '_computeValueTooltip').returns('');
       element.account = {
         _account_id: 1,
         name: 'bojack',
@@ -78,7 +73,7 @@
 
     test('deletes votes', () => {
       const deleteResponse = Promise.resolve({ok: true});
-      const deleteStub = sandbox.stub(
+      const deleteStub = sinon.stub(
           element.$.restAPI, 'deleteVote').returns(deleteResponse);
 
       element.change.removable_reviewers = [element.account];
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.js b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.js
index 24830ed..3e904a2 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.js
@@ -22,26 +22,22 @@
 
 suite('gr-labeled-autocomplete tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   test('tapping trigger focuses autocomplete', () => {
     const e = {stopPropagation: () => undefined};
-    sandbox.stub(e, 'stopPropagation');
-    sandbox.stub(element.$.autocomplete, 'focus');
+    sinon.stub(e, 'stopPropagation');
+    sinon.stub(element.$.autocomplete, 'focus');
     element._handleTriggerClick(e);
     assert.isTrue(e.stopPropagation.calledOnce);
     assert.isTrue(element.$.autocomplete.focus.calledOnce);
   });
 
   test('setText', () => {
-    sandbox.stub(element.$.autocomplete, 'setText');
+    sinon.stub(element.$.autocomplete, 'setText');
     element.setText('foo-bar');
     assert.isTrue(element.$.autocomplete.setText.calledWith('foo-bar'));
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js
index 8577c9a..4231d71 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js
@@ -21,16 +21,14 @@
 const basicFixture = fixtureFromElement('gr-lib-loader');
 
 suite('gr-lib-loader tests', () => {
-  let sandbox;
   let element;
   let resolveLoad;
   let loadStub;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
 
-    loadStub = sandbox.stub(element, '_loadScript', () =>
+    loadStub = sinon.stub(element, '_loadScript').callsFake(() =>
       new Promise(resolve => resolveLoad = resolve)
     );
 
@@ -42,7 +40,6 @@
     if (window.hljs) {
       delete window.hljs;
     }
-    sandbox.restore();
 
     // Because the element state is a singleton, clean it up.
     element._hljsState.configured = false;
@@ -51,7 +48,7 @@
   });
 
   test('only load once', done => {
-    sandbox.stub(element, '_getHLJSUrl').returns('');
+    sinon.stub(element, '_getHLJSUrl').returns('');
     const firstCallHandler = sinon.stub();
     element.getHLJS().then(firstCallHandler);
 
@@ -116,7 +113,7 @@
       let root;
 
       setup(() => {
-        sandbox.stub(element, '_getLibRoot', () => root);
+        sinon.stub(element, '_getLibRoot').callsFake(() => root);
       });
 
       test('with no root', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.js
index cb88df9..dda3324 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.js
@@ -22,19 +22,13 @@
 
 suite('gr-limited-text tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_updateTitle', () => {
-    const updateSpy = sandbox.spy(element, '_updateTitle');
+    const updateSpy = sinon.spy(element, '_updateTitle');
     element.text = 'abc 123';
     flushAsynchronousOperations();
     assert.isTrue(updateSpy.calledOnce);
@@ -78,7 +72,7 @@
   });
 
   test('when disable tooltip', () => {
-    sandbox.spy(element, '_updateTitle');
+    sinon.spy(element, '_updateTitle');
     element.text = 'abcdefghijklmn';
     element.disableTooltip = true;
     element.limit = 10;
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.js
index e4c277c..b111e84 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.js
@@ -22,19 +22,13 @@
 
 suite('gr-linked-chip tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-  });
-
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('remove fired', () => {
-    const spy = sandbox.spy();
+    const spy = sinon.spy();
     element.addEventListener('remove', spy);
     flushAsynchronousOperations();
     MockInteractions.tap(element.$.remove);
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.js
index 054e293..a295d1a 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.js
@@ -29,14 +29,14 @@
 
 suite('gr-linked-text tests', () => {
   let element;
-  let sandbox;
+
   let originalCanonicalPath;
 
   setup(() => {
     originalCanonicalPath = window.CANONICAL_PATH;
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
-    sandbox.stub(GerritNav, 'mapCommentlinks', x => x);
+
+    sinon.stub(GerritNav, 'mapCommentlinks').value( x => x);
     element.config = {
       ph: {
         match: '([Bb]ug|[Ii]ssue)\\s*#?(\\d+)',
@@ -80,7 +80,6 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     window.CANONICAL_PATH = originalCanonicalPath;
   });
 
@@ -357,8 +356,8 @@
   });
 
   test('_contentOrConfigChanged called with config', () => {
-    const contentStub = sandbox.stub(element, '_contentChanged');
-    const contentConfigStub = sandbox.stub(element, '_contentOrConfigChanged');
+    const contentStub = sinon.stub(element, '_contentChanged');
+    const contentConfigStub = sinon.stub(element, '_contentOrConfigChanged');
     element.content = 'some text';
     assert.isTrue(contentStub.called);
     assert.isTrue(contentConfigStub.called);
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.js b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.js
index ee8f78b..93451ff 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.js
@@ -23,24 +23,18 @@
 
 suite('gr-list-view tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('_computeNavLink', () => {
     const offset = 25;
     const projectsPerPage = 25;
     let filter = 'test';
     const path = '/admin/projects';
 
-    sandbox.stub(element, 'getBaseUrl', () => '');
+    sinon.stub(element, 'getBaseUrl').callsFake(() => '');
 
     assert.equal(
         element._computeNavLink(offset, 1, projectsPerPage, filter, path),
@@ -66,7 +60,7 @@
 
   test('_onValueChange', done => {
     element.path = '/admin/projects';
-    sandbox.stub(page, 'show', url => {
+    sinon.stub(page, 'show').callsFake( url => {
       assert.equal(url, '/admin/projects/q/filter:test');
       done();
     });
@@ -74,7 +68,7 @@
   });
 
   test('_filterChanged not reload when swap between falsy values', () => {
-    sandbox.stub(element, '_debounceReload');
+    sinon.stub(element, '_debounceReload');
     element.filter = null;
     element.filter = undefined;
     element.filter = '';
@@ -122,7 +116,7 @@
   });
 
   test('fires create clicked event when button tapped', () => {
-    const clickHandler = sandbox.stub();
+    const clickHandler = sinon.stub();
     element.addEventListener('create-clicked', clickHandler);
     element.createNew = true;
     flushAsynchronousOperations();
@@ -133,7 +127,7 @@
   test('next/prev links change when path changes', () => {
     const BRANCHES_PATH = '/path/to/branches';
     const TAGS_PATH = '/path/to/tags';
-    sandbox.stub(element, '_computeNavLink');
+    sinon.stub(element, '_computeNavLink');
     element.offset = 0;
     element.itemsPerPage = 25;
     element.filter = '';
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.js b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.js
index f0d5267..f3c591b 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.js
@@ -27,21 +27,15 @@
 
 suite('gr-overlay tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('events are fired on fullscreen view', done => {
-    sandbox.stub(element, '_isMobile').returns(true);
-    const openHandler = sandbox.stub();
-    const closeHandler = sandbox.stub();
+    sinon.stub(element, '_isMobile').returns(true);
+    const openHandler = sinon.stub();
+    const closeHandler = sinon.stub();
     element.addEventListener('fullscreen-overlay-opened', openHandler);
     element.addEventListener('fullscreen-overlay-closed', closeHandler);
 
@@ -58,9 +52,9 @@
   });
 
   test('events are not fired on desktop view', done => {
-    sandbox.stub(element, '_isMobile').returns(false);
-    const openHandler = sandbox.stub();
-    const closeHandler = sandbox.stub();
+    sinon.stub(element, '_isMobile').returns(false);
+    const openHandler = sinon.stub();
+    const closeHandler = sinon.stub();
     element.addEventListener('fullscreen-overlay-opened', openHandler);
     element.addEventListener('fullscreen-overlay-closed', closeHandler);
 
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.js b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.js
index 6ee4f71..2e40b27 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.js
@@ -29,30 +29,24 @@
 
 suite('gr-page-nav tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('header is not pinned just below top', () => {
-    sandbox.stub(element, '_getOffsetParent', () => 0);
-    sandbox.stub(element, '_getOffsetTop', () => 10);
-    sandbox.stub(element, '_getScrollY', () => 5);
+    sinon.stub(element, '_getOffsetParent').callsFake(() => 0);
+    sinon.stub(element, '_getOffsetTop').callsFake(() => 10);
+    sinon.stub(element, '_getScrollY').callsFake(() => 5);
     element._handleBodyScroll();
     assert.isFalse(element.$.nav.classList.contains('pinned'));
   });
 
   test('header is pinned when scroll down the page', () => {
-    sandbox.stub(element, '_getOffsetParent', () => 0);
-    sandbox.stub(element, '_getOffsetTop', () => 10);
-    sandbox.stub(element, '_getScrollY', () => 25);
+    sinon.stub(element, '_getOffsetParent').callsFake(() => 0);
+    sinon.stub(element, '_getOffsetTop').callsFake(() => 10);
+    sinon.stub(element, '_getScrollY').callsFake(() => 25);
     window.scrollY = 100;
     element._handleBodyScroll();
     assert.isTrue(element.$.nav.classList.contains('pinned'));
@@ -60,7 +54,7 @@
 
   test('header is not pinned just below top with header set', () => {
     element._headerHeight = 20;
-    sandbox.stub(element, '_getScrollY', () => 15);
+    sinon.stub(element, '_getScrollY').callsFake(() => 15);
     window.scrollY = 100;
     element._handleBodyScroll();
     assert.isFalse(element.$.nav.classList.contains('pinned'));
@@ -68,7 +62,7 @@
 
   test('header is pinned when scroll down the page with header set', () => {
     element._headerHeight = 20;
-    sandbox.stub(element, '_getScrollY', () => 25);
+    sinon.stub(element, '_getScrollY').callsFake(() => 25);
     window.scrollY = 100;
     element._handleBodyScroll();
     assert.isTrue(element.$.nav.classList.contains('pinned'));
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.js b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.js
index 111843b..bfdbe41 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.js
@@ -22,18 +22,14 @@
 
 suite('gr-repo-branch-picker tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
   });
 
-  teardown(() => { sandbox.restore(); });
-
   suite('_getRepoSuggestions', () => {
     setup(() => {
-      sandbox.stub(element.$.restAPI, 'getRepos')
+      sinon.stub(element.$.restAPI, 'getRepos')
           .returns(Promise.resolve([
             {
               id: 'plugins%2Favatars-external',
@@ -69,7 +65,7 @@
 
   suite('_getRepoBranchesSuggestions', () => {
     setup(() => {
-      sandbox.stub(element.$.restAPI, 'getRepoBranches')
+      sinon.stub(element.$.restAPI, 'getRepoBranches')
           .returns(Promise.resolve([
             {ref: 'refs/heads/stable-2.10'},
             {ref: 'refs/heads/stable-2.11'},
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js
index b40daaa..af2efae 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js
@@ -22,22 +22,16 @@
 
 suite('gr-auth', () => {
   let auth;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     auth = authService;
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   suite('Auth class methods', () => {
     let fakeFetch;
     setup(() => {
       auth = new Auth();
-      fakeFetch = sandbox.stub(window, 'fetch');
+      fakeFetch = sinon.stub(window, 'fetch');
     });
 
     test('auth-check returns 403', done => {
@@ -83,7 +77,7 @@
     setup(() => {
       auth = new Auth();
       clock = sinon.useFakeTimers();
-      fakeFetch = sandbox.stub(window, 'fetch');
+      fakeFetch = sinon.stub(window, 'fetch');
     });
 
     test('cache auth-check result', done => {
@@ -218,7 +212,7 @@
 
   suite('default (xsrf token header)', () => {
     setup(() => {
-      sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
+      sinon.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
     });
 
     test('GET', done => {
@@ -231,7 +225,7 @@
     });
 
     test('POST', done => {
-      sandbox.stub(auth, '_getCookie')
+      sinon.stub(auth, '_getCookie')
           .withArgs('XSRF_TOKEN')
           .returns('foobar');
       auth.fetch('/url', {method: 'POST'}).then(() => {
@@ -246,7 +240,7 @@
 
   suite('cors (access token)', () => {
     setup(() => {
-      sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
+      sinon.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
     });
 
     let getToken;
@@ -259,14 +253,14 @@
     };
 
     setup(() => {
-      getToken = sandbox.stub();
+      getToken = sinon.stub();
       getToken.returns(Promise.resolve(makeToken()));
       auth.setup(getToken);
     });
 
     test('base url support', done => {
       const baseUrl = 'http://foo';
-      sandbox.stub(BaseUrlBehavior, 'getBaseUrl').returns(baseUrl);
+      sinon.stub(BaseUrlBehavior, 'getBaseUrl').returns(baseUrl);
       auth.fetch(baseUrl + '/url', {bar: 'bar'}).then(() => {
         const [url] = fetch.lastCall.args;
         assert.equal(url, 'http://foo/a/url?access_token=zbaz');
@@ -303,7 +297,7 @@
     });
 
     test('getToken refreshes token', done => {
-      sandbox.stub(auth, '_isTokenValid');
+      sinon.stub(auth, '_isTokenValid');
       auth._isTokenValid
           .onFirstCall().returns(true)
           .onSecondCall()
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.js
index 3ddc42b..e5217a4 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.js
@@ -20,7 +20,6 @@
 
 suite('gr-etag-decorator', () => {
   let etag;
-  let sandbox;
 
   const fakeRequest = (opt_etag, opt_status) => {
     const headers = new Headers();
@@ -32,14 +31,9 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     etag = new GrEtagDecorator();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('exists', () => {
     assert.isOk(etag);
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index fe2c3ea..e792c4d 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -58,6 +58,23 @@
 let projectLookup = {}; // Shared across instances.
 
 export function _testOnlyResetGrRestApiSharedObjects() {
+  for (const key in fetchPromisesCache._data) {
+    if (fetchPromisesCache._data.hasOwnProperty(key)) {
+      // reject already fulfilled promise does nothing
+      fetchPromisesCache._data[key].reject();
+    }
+  }
+
+  for (const key in pendingRequest) {
+    if (!pendingRequest.hasOwnProperty(key)) {
+      continue;
+    }
+    for (const req of pendingRequest[key]) {
+      // reject already fulfilled promise does nothing
+      req.reject();
+    }
+  }
+
   siteBasedCache = new SiteBasedCache();
   fetchPromisesCache = new FetchPromisesCache();
   pendingRequest = {};
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
index 8dd4ff9..639b768 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
@@ -25,7 +25,7 @@
 
 suite('gr-rest-api-interface tests', () => {
   let element;
-  let sandbox;
+
   let ctr = 0;
   let originalCanonicalPath;
 
@@ -35,40 +35,39 @@
     originalCanonicalPath = window.CANONICAL_PATH;
     window.CANONICAL_PATH = `test${ctr}`;
 
-    sandbox = sinon.sandbox.create();
     const testJSON = ')]}\'\n{"hello": "bonjour"}';
-    sandbox.stub(window, 'fetch').returns(Promise.resolve({
+    sinon.stub(window, 'fetch').returns(Promise.resolve({
       ok: true,
       text() {
         return Promise.resolve(testJSON);
       },
     }));
     // fake auth
-    sandbox.stub(authService, 'authCheck').returns(Promise.resolve(true));
+    sinon.stub(authService, 'authCheck').returns(Promise.resolve(true));
     element = basicFixture.instantiate();
     element._projectLookup = {};
   });
 
   teardown(() => {
-    sandbox.restore();
     window.CANONICAL_PATH = originalCanonicalPath;
   });
 
   test('parent diff comments are properly grouped', done => {
-    sandbox.stub(element._restApiHelper, 'fetchJSON', () => Promise.resolve({
-      '/COMMIT_MSG': [],
-      'sieve.go': [
-        {
-          updated: '2017-02-03 22:32:28.000000000',
-          message: 'this isn’t quite right',
-        },
-        {
-          side: 'PARENT',
-          message: 'how did this work in the first place?',
-          updated: '2017-02-03 22:33:28.000000000',
-        },
-      ],
-    }));
+    sinon.stub(element._restApiHelper, 'fetchJSON')
+        .callsFake(() => Promise.resolve({
+          '/COMMIT_MSG': [],
+          'sieve.go': [
+            {
+              updated: '2017-02-03 22:32:28.000000000',
+              message: 'this isn’t quite right',
+            },
+            {
+              side: 'PARENT',
+              message: 'how did this work in the first place?',
+              updated: '2017-02-03 22:33:28.000000000',
+            },
+          ],
+        }));
     element._getDiffComments('42', '', 'PARENT', 1, 'sieve.go').then(
         obj => {
           assert.equal(obj.baseComments.length, 1);
@@ -194,9 +193,9 @@
   });
 
   test('differing patch diff comments are properly grouped', done => {
-    sandbox.stub(element, 'getFromProjectLookup')
+    sinon.stub(element, 'getFromProjectLookup')
         .returns(Promise.resolve('test'));
-    sandbox.stub(element._restApiHelper, 'fetchJSON', request => {
+    sinon.stub(element._restApiHelper, 'fetchJSON').callsFake( request => {
       const url = request.url;
       if (url === '/changes/test~42/revisions/1') {
         return Promise.resolve({
@@ -311,7 +310,7 @@
   });
 
   test('server error', done => {
-    const getResponseObjectStub = sandbox.stub(element, 'getResponseObject');
+    const getResponseObjectStub = sinon.stub(element, 'getResponseObject');
     window.fetch.returns(Promise.resolve({ok: false}));
     const serverErrorEventPromise = new Promise(resolve => {
       element.addEventListener('server-error', resolve);
@@ -325,8 +324,8 @@
   });
 
   test('legacy n,z key in change url is replaced', async () => {
-    sandbox.stub(element, 'getConfig', async () => { return {}; });
-    const stub = sandbox.stub(element._restApiHelper, 'fetchJSON')
+    sinon.stub(element, 'getConfig').callsFake( async () => { return {}; });
+    const stub = sinon.stub(element._restApiHelper, 'fetchJSON')
         .returns(Promise.resolve([]));
     await element.getChanges(1, null, 'n,z');
     assert.equal(stub.lastCall.args[0].params.S, 0);
@@ -334,7 +333,7 @@
 
   test('saveDiffPreferences invalidates cache line', () => {
     const cacheKey = '/accounts/self/preferences.diff';
-    const sendStub = sandbox.stub(element._restApiHelper, 'send');
+    const sendStub = sinon.stub(element._restApiHelper, 'send');
     element._cache.set(cacheKey, {tab_size: 4});
     element.saveDiffPreferences({tab_size: 8});
     assert.isTrue(sendStub.called);
@@ -344,8 +343,8 @@
   test('getAccount when resp is null does not add anything to the cache',
       done => {
         const cacheKey = '/accounts/self/detail';
-        const stub = sandbox.stub(element._restApiHelper, 'fetchCacheURL',
-            () => Promise.resolve());
+        const stub = sinon.stub(element._restApiHelper, 'fetchCacheURL')
+            .callsFake(() => Promise.resolve());
 
         element.getAccount().then(() => {
           assert.isTrue(stub.called);
@@ -360,8 +359,8 @@
   test('getAccount does not add to the cache when resp.status is 403',
       done => {
         const cacheKey = '/accounts/self/detail';
-        const stub = sandbox.stub(element._restApiHelper, 'fetchCacheURL',
-            () => Promise.resolve());
+        const stub = sinon.stub(element._restApiHelper, 'fetchCacheURL')
+            .callsFake(() => Promise.resolve());
 
         element.getAccount().then(() => {
           assert.isTrue(stub.called);
@@ -374,7 +373,7 @@
 
   test('getAccount when resp is successful', done => {
     const cacheKey = '/accounts/self/detail';
-    const stub = sandbox.stub(element._restApiHelper, 'fetchCacheURL',
+    const stub = sinon.stub(element._restApiHelper, 'fetchCacheURL').callsFake(
         () => Promise.resolve());
 
     element.getAccount().then(response => {
@@ -388,12 +387,13 @@
   });
 
   const preferenceSetup = function(testJSON, loggedIn, smallScreen) {
-    sandbox.stub(element, 'getLoggedIn', () => Promise.resolve(loggedIn));
-    sandbox.stub(element, '_isNarrowScreen', () => smallScreen);
-    sandbox.stub(
+    sinon.stub(element, 'getLoggedIn')
+        .callsFake(() => Promise.resolve(loggedIn));
+    sinon.stub(element, '_isNarrowScreen').callsFake(() => smallScreen);
+    sinon.stub(
         element._restApiHelper,
-        'fetchCacheURL',
-        () => Promise.resolve(testJSON));
+        'fetchCacheURL')
+        .callsFake(() => Promise.resolve(testJSON));
   };
 
   test('getPreferences returns correctly on small screens logged in',
@@ -456,14 +456,14 @@
       });
 
   test('savPreferences normalizes download scheme', () => {
-    const sendStub = sandbox.stub(element._restApiHelper, 'send');
+    const sendStub = sinon.stub(element._restApiHelper, 'send');
     element.savePreferences({download_scheme: 'HTTP'});
     assert.isTrue(sendStub.called);
     assert.equal(sendStub.lastCall.args[0].body.download_scheme, 'http');
   });
 
   test('getDiffPreferences returns correct defaults', done => {
-    sandbox.stub(element, 'getLoggedIn', () => Promise.resolve(false));
+    sinon.stub(element, 'getLoggedIn').callsFake(() => Promise.resolve(false));
 
     element.getDiffPreferences().then(obj => {
       assert.equal(obj.auto_hide_diff_table_header, true);
@@ -485,14 +485,14 @@
   });
 
   test('saveDiffPreferences set show_tabs to false', () => {
-    const sendStub = sandbox.stub(element._restApiHelper, 'send');
+    const sendStub = sinon.stub(element._restApiHelper, 'send');
     element.saveDiffPreferences({show_tabs: false});
     assert.isTrue(sendStub.called);
     assert.equal(sendStub.lastCall.args[0].body.show_tabs, false);
   });
 
   test('getEditPreferences returns correct defaults', done => {
-    sandbox.stub(element, 'getLoggedIn', () => Promise.resolve(false));
+    sinon.stub(element, 'getLoggedIn').callsFake(() => Promise.resolve(false));
 
     element.getEditPreferences().then(obj => {
       assert.equal(obj.auto_close_brackets, false);
@@ -516,14 +516,14 @@
   });
 
   test('saveEditPreferences set show_tabs to false', () => {
-    const sendStub = sandbox.stub(element._restApiHelper, 'send');
+    const sendStub = sinon.stub(element._restApiHelper, 'send');
     element.saveEditPreferences({show_tabs: false});
     assert.isTrue(sendStub.called);
     assert.equal(sendStub.lastCall.args[0].body.show_tabs, false);
   });
 
   test('confirmEmail', () => {
-    const sendStub = sandbox.spy(element._restApiHelper, 'send');
+    const sendStub = sinon.spy(element._restApiHelper, 'send');
     element.confirmEmail('foo');
     assert.isTrue(sendStub.calledOnce);
     assert.equal(sendStub.lastCall.args[0].method, 'PUT');
@@ -533,7 +533,7 @@
   });
 
   test('setAccountStatus', () => {
-    const sendStub = sandbox.stub(element._restApiHelper, 'send')
+    const sendStub = sinon.stub(element._restApiHelper, 'send')
         .returns(Promise.resolve('OOO'));
     element._cache.set('/accounts/self/detail', {});
     return element.setAccountStatus('OOO').then(() => {
@@ -552,7 +552,8 @@
   suite('draft comments', () => {
     test('_sendDiffDraftRequest pending requests tracked', () => {
       const obj = element._pendingRequests;
-      sandbox.stub(element, '_getChangeURLAndSend', () => mockPromise());
+      sinon.stub(element, '_getChangeURLAndSend')
+          .callsFake(() => mockPromise());
       assert.notOk(element.hasPendingDiffDrafts());
 
       element._sendDiffDraftRequest(null, null, null, {});
@@ -574,8 +575,8 @@
     suite('_failForCreate200', () => {
       test('_sendDiffDraftRequest checks for 200 on create', () => {
         const sendPromise = Promise.resolve();
-        sandbox.stub(element, '_getChangeURLAndSend').returns(sendPromise);
-        const failStub = sandbox.stub(element, '_failForCreate200')
+        sinon.stub(element, '_getChangeURLAndSend').returns(sendPromise);
+        const failStub = sinon.stub(element, '_failForCreate200')
             .returns(Promise.resolve());
         return element._sendDiffDraftRequest('PUT', 123, 4, {}).then(() => {
           assert.isTrue(failStub.calledOnce);
@@ -584,9 +585,9 @@
       });
 
       test('_sendDiffDraftRequest no checks for 200 on non create', () => {
-        sandbox.stub(element, '_getChangeURLAndSend')
+        sinon.stub(element, '_getChangeURLAndSend')
             .returns(Promise.resolve());
-        const failStub = sandbox.stub(element, '_failForCreate200')
+        const failStub = sinon.stub(element, '_failForCreate200')
             .returns(Promise.resolve());
         return element._sendDiffDraftRequest('PUT', 123, 4, {id: '123'})
             .then(() => {
@@ -638,9 +639,9 @@
     const change_num = '1';
     const file_name = 'index.php';
     const file_contents = '<?php';
-    sandbox.stub(element._restApiHelper, 'send').returns(
+    sinon.stub(element._restApiHelper, 'send').returns(
         Promise.resolve([change_num, file_name, file_contents]));
-    sandbox.stub(element, 'getResponseObject')
+    sinon.stub(element, 'getResponseObject')
         .returns(Promise.resolve([change_num, file_name, file_contents]));
     element._cache.set('/changes/' + change_num + '/edit/' + file_name, {});
     return element.saveChangeEdit(change_num, file_name, file_contents)
@@ -659,9 +660,9 @@
     element._projectLookup = {1: 'test'};
     const change_num = '1';
     const message = 'this is a commit message';
-    sandbox.stub(element._restApiHelper, 'send').returns(
+    sinon.stub(element._restApiHelper, 'send').returns(
         Promise.resolve([change_num, message]));
-    sandbox.stub(element, 'getResponseObject')
+    sinon.stub(element, 'getResponseObject')
         .returns(Promise.resolve([change_num, message]));
     element._cache.set('/changes/' + change_num + '/message', {});
     return element.putChangeCommitMessage(change_num, message).then(() => {
@@ -678,9 +679,9 @@
     element._projectLookup = {1: 'test'};
     const change_num = '1';
     const messageId = 'abc';
-    sandbox.stub(element._restApiHelper, 'send').returns(
+    sinon.stub(element._restApiHelper, 'send').returns(
         Promise.resolve([change_num, messageId]));
-    sandbox.stub(element, 'getResponseObject')
+    sinon.stub(element, 'getResponseObject')
         .returns(Promise.resolve([change_num, messageId]));
     return element.deleteChangeCommitMessage(change_num, messageId).then(() => {
       assert.isTrue(element._restApiHelper.send.calledOnce);
@@ -694,7 +695,7 @@
   });
 
   test('startWorkInProgress', () => {
-    const sendStub = sandbox.stub(element, '_getChangeURLAndSend')
+    const sendStub = sinon.stub(element, '_getChangeURLAndSend')
         .returns(Promise.resolve('ok'));
     element.startWorkInProgress('42');
     assert.isTrue(sendStub.calledOnce);
@@ -715,7 +716,7 @@
   });
 
   test('startReview', () => {
-    const sendStub = sandbox.stub(element, '_getChangeURLAndSend')
+    const sendStub = sinon.stub(element, '_getChangeURLAndSend')
         .returns(Promise.resolve({}));
     element.startReview('42', {message: 'Please review.'});
     assert.isTrue(sendStub.calledOnce);
@@ -728,7 +729,7 @@
   });
 
   test('deleteComment', () => {
-    const sendStub = sandbox.stub(element, '_getChangeURLAndSend')
+    const sendStub = sinon.stub(element, '_getChangeURLAndSend')
         .returns(Promise.resolve('some response'));
     return element.deleteComment('foo', 'bar', '01234', 'removal reason')
         .then(response => {
@@ -745,7 +746,7 @@
   });
 
   test('createRepo encodes name', () => {
-    const sendStub = sandbox.stub(element._restApiHelper, 'send')
+    const sendStub = sinon.stub(element._restApiHelper, 'send')
         .returns(Promise.resolve());
     return element.createRepo({name: 'x/y'}).then(() => {
       assert.isTrue(sendStub.calledOnce);
@@ -754,7 +755,7 @@
   });
 
   test('queryChangeFiles', () => {
-    const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+    const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
         .returns(Promise.resolve());
     return element.queryChangeFiles('42', 'edit', 'test/path.js').then(() => {
       assert.equal(fetchStub.lastCall.args[0].changeNum, '42');
@@ -806,7 +807,7 @@
     let fetchCacheURLStub;
     setup(() => {
       fetchCacheURLStub =
-          sandbox.stub(element._restApiHelper, 'fetchCacheURL');
+          sinon.stub(element._restApiHelper, 'fetchCacheURL');
     });
 
     test('normal use', () => {
@@ -893,7 +894,7 @@
     let fetchCacheURLStub;
     setup(() => {
       fetchCacheURLStub =
-          sandbox.stub(element._restApiHelper, 'fetchCacheURL');
+          sinon.stub(element._restApiHelper, 'fetchCacheURL');
     });
 
     test('normal use', () => {
@@ -922,13 +923,13 @@
   });
 
   test('gerrit auth is used', () => {
-    sandbox.stub(authService, 'fetch').returns(Promise.resolve());
+    sinon.stub(authService, 'fetch').returns(Promise.resolve());
     element._restApiHelper.fetchJSON({url: 'foo'});
     assert(authService.fetch.called);
   });
 
   test('getSuggestedAccounts does not return _fetchJSON', () => {
-    const _fetchJSONSpy = sandbox.spy(element._restApiHelper, 'fetchJSON');
+    const _fetchJSONSpy = sinon.spy(element._restApiHelper, 'fetchJSON');
     return element.getSuggestedAccounts().then(accts => {
       assert.isFalse(_fetchJSONSpy.called);
       assert.equal(accts.length, 0);
@@ -936,8 +937,8 @@
   });
 
   test('_fetchJSON gets called by getSuggestedAccounts', () => {
-    const _fetchJSONStub = sandbox.stub(element._restApiHelper, 'fetchJSON',
-        () => Promise.resolve());
+    const _fetchJSONStub = sinon.stub(element._restApiHelper, 'fetchJSON')
+        .callsFake(() => Promise.resolve());
     return element.getSuggestedAccounts('own').then(() => {
       assert.deepEqual(_fetchJSONStub.lastCall.args[0].params, {
         q: 'own',
@@ -951,15 +952,15 @@
       let toHexStub;
 
       setup(() => {
-        toHexStub = sandbox.stub(element, 'listChangesOptionsToHex',
+        toHexStub = sinon.stub(element, 'listChangesOptionsToHex').callsFake(
             options => 'deadbeef');
-        sandbox.stub(element, '_getChangeDetail',
+        sinon.stub(element, '_getChangeDetail').callsFake(
             async (changeNum, options) => { return {changeNum, options}; });
       });
 
       test('signed pushes disabled', async () => {
         const {PUSH_CERTIFICATES} = element.ListChangesOption;
-        sandbox.stub(element, 'getConfig', async () => { return {}; });
+        sinon.stub(element, 'getConfig').callsFake( async () => { return {}; });
         const {changeNum, options} = await element.getChangeDetail(123);
         assert.strictEqual(123, changeNum);
         assert.strictEqual('deadbeef', options);
@@ -969,7 +970,7 @@
 
       test('signed pushes enabled', async () => {
         const {PUSH_CERTIFICATES} = element.ListChangesOption;
-        sandbox.stub(element, 'getConfig', async () => {
+        sinon.stub(element, 'getConfig').callsFake( async () => {
           return {receive: {enable_signed_push: true}};
         });
         const {changeNum, options} = await element.getChangeDetail(123);
@@ -981,7 +982,7 @@
     });
 
     test('GrReviewerUpdatesParser.parse is used', () => {
-      sandbox.stub(GrReviewerUpdatesParser, 'parse').returns(
+      sinon.stub(GrReviewerUpdatesParser, 'parse').returns(
           Promise.resolve('foo'));
       return element.getChangeDetail(42).then(result => {
         assert.isTrue(GrReviewerUpdatesParser.parse.calledOnce);
@@ -995,8 +996,8 @@
       const expectedUrl =
           window.CANONICAL_PATH + '/changes/test~4321/detail?'+
           '0=5&1=1&2=6&3=7&4=1&5=4';
-      sandbox.stub(element._etags, 'getOptions');
-      sandbox.stub(element._etags, 'collect');
+      sinon.stub(element._etags, 'getOptions');
+      sinon.stub(element._etags, 'collect');
       return element._getChangeDetail(changeNum, '516714').then(() => {
         assert.isTrue(element._etags.getOptions.calledWithExactly(
             expectedUrl));
@@ -1006,9 +1007,9 @@
 
     test('_getChangeDetail calls errFn on 500', () => {
       const errFn = sinon.stub();
-      sandbox.stub(element, 'getChangeActionURL')
+      sinon.stub(element, 'getChangeActionURL')
           .returns(Promise.resolve(''));
-      sandbox.stub(element._restApiHelper, 'fetchRawJSON')
+      sinon.stub(element._restApiHelper, 'fetchRawJSON')
           .returns(Promise.resolve({ok: false, status: 500}));
       return element._getChangeDetail(123, '516714', errFn).then(() => {
         assert.isTrue(errFn.called);
@@ -1016,13 +1017,13 @@
     });
 
     test('_getChangeDetail populates _projectLookup', () => {
-      sandbox.stub(element, 'getChangeActionURL')
+      sinon.stub(element, 'getChangeActionURL')
           .returns(Promise.resolve(''));
-      sandbox.stub(element._restApiHelper, 'fetchRawJSON')
+      sinon.stub(element._restApiHelper, 'fetchRawJSON')
           .returns(Promise.resolve({ok: true}));
 
       const mockResponse = {_number: 1, project: 'test'};
-      sandbox.stub(element._restApiHelper, 'readResponsePayload')
+      sinon.stub(element._restApiHelper, 'readResponsePayload')
           .returns(Promise.resolve({
             parsed: mockResponse,
             raw: JSON.stringify(mockResponse),
@@ -1044,16 +1045,16 @@
         const mockResponse = {foo: 'bar', baz: 42};
         mockResponseSerial = element.JSON_PREFIX +
             JSON.stringify(mockResponse);
-        sandbox.stub(element._restApiHelper, 'urlWithParams')
+        sinon.stub(element._restApiHelper, 'urlWithParams')
             .returns(requestUrl);
-        sandbox.stub(element, 'getChangeActionURL')
+        sinon.stub(element, 'getChangeActionURL')
             .returns(Promise.resolve(requestUrl));
-        collectSpy = sandbox.spy(element._etags, 'collect');
-        getPayloadSpy = sandbox.spy(element._etags, 'getCachedPayload');
+        collectSpy = sinon.spy(element._etags, 'collect');
+        getPayloadSpy = sinon.spy(element._etags, 'getCachedPayload');
       });
 
       test('contributes to cache', () => {
-        sandbox.stub(element._restApiHelper, 'fetchRawJSON')
+        sinon.stub(element._restApiHelper, 'fetchRawJSON')
             .returns(Promise.resolve({
               text: () => Promise.resolve(mockResponseSerial),
               status: 200,
@@ -1069,7 +1070,7 @@
       });
 
       test('uses cache on HTTP 304', () => {
-        sandbox.stub(element._restApiHelper, 'fetchRawJSON')
+        sinon.stub(element._restApiHelper, 'fetchRawJSON')
             .returns(Promise.resolve({
               text: () => Promise.resolve(mockResponseSerial),
               status: 304,
@@ -1091,7 +1092,7 @@
 
   suite('getFromProjectLookup', () => {
     test('getChange fails', () => {
-      sandbox.stub(element, 'getChange')
+      sinon.stub(element, 'getChange')
           .returns(Promise.resolve(null));
       return element.getFromProjectLookup().then(val => {
         assert.strictEqual(val, undefined);
@@ -1100,7 +1101,7 @@
     });
 
     test('getChange succeeds, no project', () => {
-      sandbox.stub(element, 'getChange').returns(Promise.resolve(null));
+      sinon.stub(element, 'getChange').returns(Promise.resolve(null));
       return element.getFromProjectLookup().then(val => {
         assert.strictEqual(val, undefined);
         assert.deepEqual(element._projectLookup, {});
@@ -1108,7 +1109,7 @@
     });
 
     test('getChange succeeds with project', () => {
-      sandbox.stub(element, 'getChange')
+      sinon.stub(element, 'getChange')
           .returns(Promise.resolve({project: 'project'}));
       return element.getFromProjectLookup('test').then(val => {
         assert.equal(val, 'project');
@@ -1119,7 +1120,7 @@
 
   suite('getChanges populates _projectLookup', () => {
     test('multiple queries', () => {
-      sandbox.stub(element._restApiHelper, 'fetchJSON')
+      sinon.stub(element._restApiHelper, 'fetchJSON')
           .returns(Promise.resolve([
             [
               {_number: 1, project: 'test'},
@@ -1139,7 +1140,7 @@
     });
 
     test('no query', () => {
-      sandbox.stub(element._restApiHelper, 'fetchJSON')
+      sinon.stub(element._restApiHelper, 'fetchJSON')
           .returns(Promise.resolve([
             {_number: 1, project: 'test'},
             {_number: 2, project: 'test'},
@@ -1159,7 +1160,7 @@
 
   test('_getChangeURLAndFetch', () => {
     element._projectLookup = {1: 'test'};
-    const fetchStub = sandbox.stub(element._restApiHelper, 'fetchJSON')
+    const fetchStub = sinon.stub(element._restApiHelper, 'fetchJSON')
         .returns(Promise.resolve());
     const req = {changeNum: 1, endpoint: '/test', patchNum: 1};
     return element._getChangeURLAndFetch(req).then(() => {
@@ -1170,7 +1171,7 @@
 
   test('_getChangeURLAndSend', () => {
     element._projectLookup = {1: 'test'};
-    const sendStub = sandbox.stub(element._restApiHelper, 'send')
+    const sendStub = sinon.stub(element._restApiHelper, 'send')
         .returns(Promise.resolve());
 
     const req = {
@@ -1208,7 +1209,7 @@
   });
 
   test('setChangeTopic', () => {
-    const sendSpy = sandbox.spy(element, '_getChangeURLAndSend');
+    const sendSpy = sinon.spy(element, '_getChangeURLAndSend');
     return element.setChangeTopic(123, 'foo-bar').then(() => {
       assert.isTrue(sendSpy.calledOnce);
       assert.deepEqual(sendSpy.lastCall.args[0].body, {topic: 'foo-bar'});
@@ -1216,7 +1217,7 @@
   });
 
   test('setChangeHashtag', () => {
-    const sendSpy = sandbox.spy(element, '_getChangeURLAndSend');
+    const sendSpy = sinon.spy(element, '_getChangeURLAndSend');
     return element.setChangeHashtag(123, 'foo-bar').then(() => {
       assert.isTrue(sendSpy.calledOnce);
       assert.equal(sendSpy.lastCall.args[0].body, 'foo-bar');
@@ -1224,7 +1225,7 @@
   });
 
   test('generateAccountHttpPassword', () => {
-    const sendSpy = sandbox.spy(element._restApiHelper, 'send');
+    const sendSpy = sinon.spy(element._restApiHelper, 'send');
     return element.generateAccountHttpPassword().then(() => {
       assert.isTrue(sendSpy.calledOnce);
       assert.deepEqual(sendSpy.lastCall.args[0].body, {generate: true});
@@ -1233,7 +1234,7 @@
 
   suite('getChangeFiles', () => {
     test('patch only', () => {
-      const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+      const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
           .returns(Promise.resolve());
       const range = {basePatchNum: 'PARENT', patchNum: 2};
       return element.getChangeFiles(123, range).then(() => {
@@ -1244,7 +1245,7 @@
     });
 
     test('simple range', () => {
-      const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+      const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
           .returns(Promise.resolve());
       const range = {basePatchNum: 4, patchNum: 5};
       return element.getChangeFiles(123, range).then(() => {
@@ -1257,7 +1258,7 @@
     });
 
     test('parent index', () => {
-      const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+      const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
           .returns(Promise.resolve());
       const range = {basePatchNum: -3, patchNum: 5};
       return element.getChangeFiles(123, range).then(() => {
@@ -1272,7 +1273,7 @@
 
   suite('getDiff', () => {
     test('patchOnly', () => {
-      const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+      const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
           .returns(Promise.resolve());
       return element.getDiff(123, 'PARENT', 2, 'foo/bar.baz').then(() => {
         assert.isTrue(fetchStub.calledOnce);
@@ -1284,7 +1285,7 @@
     });
 
     test('simple range', () => {
-      const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+      const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
           .returns(Promise.resolve());
       return element.getDiff(123, 4, 5, 'foo/bar.baz').then(() => {
         assert.isTrue(fetchStub.calledOnce);
@@ -1296,7 +1297,7 @@
     });
 
     test('parent index', () => {
-      const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+      const fetchStub = sinon.stub(element, '_getChangeURLAndFetch')
           .returns(Promise.resolve());
       return element.getDiff(123, -3, 5, 'foo/bar.baz').then(() => {
         assert.isTrue(fetchStub.calledOnce);
@@ -1309,7 +1310,7 @@
   });
 
   test('getDashboard', () => {
-    const fetchCacheURLStub = sandbox.stub(element._restApiHelper,
+    const fetchCacheURLStub = sinon.stub(element._restApiHelper,
         'fetchCacheURL');
     element.getDashboard('gerrit/project', 'default:main');
     assert.isTrue(fetchCacheURLStub.calledOnce);
@@ -1319,7 +1320,7 @@
   });
 
   test('getFileContent', () => {
-    sandbox.stub(element, '_getChangeURLAndSend')
+    sinon.stub(element, '_getChangeURLAndSend')
         .returns(Promise.resolve({
           ok: 'true',
           headers: {
@@ -1331,7 +1332,7 @@
           },
         }));
 
-    sandbox.stub(element, 'getResponseObject')
+    sinon.stub(element, 'getResponseObject')
         .returns(Promise.resolve('new content'));
 
     const edit = element.getFileContent('1', 'tst/path', 'EDIT').then(res => {
@@ -1354,8 +1355,8 @@
       done();
     };
     element.addEventListener('server-error', handler);
-    sandbox.stub(authService, 'fetch').returns(Promise.resolve(res));
-    sandbox.stub(element, '_changeBaseURL').returns(Promise.resolve(''));
+    sinon.stub(authService, 'fetch').returns(Promise.resolve(res));
+    sinon.stub(element, '_changeBaseURL').returns(Promise.resolve(''));
     element.getFileContent('1', 'tst/path', '1').then(() => {
       flushAsynchronousOperations();
 
@@ -1366,9 +1367,9 @@
 
   test('getChangeFilesOrEditFiles is edit-sensitive', () => {
     const fn = element.getChangeOrEditFiles.bind(element);
-    const getChangeFilesStub = sandbox.stub(element, 'getChangeFiles')
+    const getChangeFilesStub = sinon.stub(element, 'getChangeFiles')
         .returns(Promise.resolve({}));
-    const getChangeEditFilesStub = sandbox.stub(element, 'getChangeEditFiles')
+    const getChangeEditFilesStub = sinon.stub(element, 'getChangeEditFiles')
         .returns(Promise.resolve({}));
 
     return fn('1', {patchNum: 'edit'}).then(() => {
@@ -1382,13 +1383,13 @@
   });
 
   test('_fetch forwards request and logs', () => {
-    const logStub = sandbox.stub(element._restApiHelper, '_logCall');
+    const logStub = sinon.stub(element._restApiHelper, '_logCall');
     const response = {status: 404, text: sinon.stub()};
     const url = 'my url';
     const fetchOptions = {method: 'DELETE'};
-    sandbox.stub(element._auth, 'fetch').returns(Promise.resolve(response));
+    sinon.stub(element._auth, 'fetch').returns(Promise.resolve(response));
     const startTime = 123;
-    sandbox.stub(Date, 'now').returns(startTime);
+    sinon.stub(Date, 'now').returns(startTime);
     const req = {url, fetchOptions};
     return element._restApiHelper.fetch(req).then(() => {
       assert.isTrue(logStub.calledOnce);
@@ -1398,7 +1399,7 @@
   });
 
   test('_logCall only reports requests with anonymized URLss', () => {
-    sandbox.stub(Date, 'now').returns(200);
+    sinon.stub(Date, 'now').returns(200);
     const handler = sinon.stub();
     element.addEventListener('rpc-log', handler);
 
@@ -1412,10 +1413,10 @@
   });
 
   test('saveChangeStarred', async () => {
-    sandbox.stub(element, 'getFromProjectLookup')
+    sinon.stub(element, 'getFromProjectLookup')
         .returns(Promise.resolve('test'));
     const sendStub =
-        sandbox.stub(element._restApiHelper, 'send').returns(Promise.resolve());
+        sinon.stub(element._restApiHelper, 'send').returns(Promise.resolve());
 
     await element.saveChangeStarred(123, true);
     assert.isTrue(sendStub.calledOnce);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js
index 076f0bd..8acba73 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js
@@ -22,13 +22,12 @@
 
 suite('gr-rest-api-helper tests', () => {
   let helper;
-  let sandbox;
+
   let cache;
   let fetchPromisesCache;
   let originalCanonicalPath;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     cache = new SiteBasedCache();
     fetchPromisesCache = new FetchPromisesCache();
 
@@ -41,7 +40,7 @@
     };
 
     const testJSON = ')]}\'\n{"hello": "bonjour"}';
-    sandbox.stub(window, 'fetch').returns(Promise.resolve({
+    sinon.stub(window, 'fetch').returns(Promise.resolve({
       ok: true,
       text() {
         return Promise.resolve(testJSON);
@@ -53,13 +52,12 @@
   });
 
   teardown(() => {
-    sandbox.restore();
     window.CANONICAL_PATH = originalCanonicalPath;
   });
 
   suite('fetchJSON()', () => {
     test('Sets header to accept application/json', () => {
-      const authFetchStub = sandbox.stub(helper._auth, 'fetch')
+      const authFetchStub = sinon.stub(helper._auth, 'fetch')
           .returns(Promise.resolve());
       helper.fetchJSON({url: '/dummy/url'});
       assert.isTrue(authFetchStub.called);
@@ -68,7 +66,7 @@
     });
 
     test('Use header option accept when provided', () => {
-      const authFetchStub = sandbox.stub(helper._auth, 'fetch')
+      const authFetchStub = sinon.stub(helper._auth, 'fetch')
           .returns(Promise.resolve());
       const headers = new Headers();
       headers.append('Accept', '*/*');
@@ -89,7 +87,7 @@
 
   test('cached results', done => {
     let n = 0;
-    sandbox.stub(helper, 'fetchJSON', () => Promise.resolve(++n));
+    sinon.stub(helper, 'fetchJSON').callsFake(() => Promise.resolve(++n));
     const promises = [];
     promises.push(helper.fetchCacheURL('/foo'));
     promises.push(helper.fetchCacheURL('/foo'));
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js
index f6da64b..f408a97 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js
@@ -20,24 +20,19 @@
 import {parseDate} from '../../../utils/date-util.js';
 
 suite('gr-reviewer-updates-parser tests', () => {
-  let sandbox;
   let instance;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
 
-  teardown(() => {
-    sandbox.restore();
   });
 
   test('ignores changes without messages', () => {
     const change = {};
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_filterRemovedMessages');
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_groupUpdates');
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_formatUpdates');
     assert.strictEqual(GrReviewerUpdatesParser.parse(change), change);
     assert.isFalse(
@@ -52,11 +47,11 @@
     const change = {
       messages: [],
     };
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_filterRemovedMessages');
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_groupUpdates');
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_formatUpdates');
     assert.strictEqual(GrReviewerUpdatesParser.parse(change), change);
     assert.isFalse(
@@ -72,11 +67,11 @@
       messages: [],
       reviewer_updates: [],
     };
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_filterRemovedMessages');
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_groupUpdates');
-    sandbox.stub(
+    sinon.stub(
         GrReviewerUpdatesParser.prototype, '_formatUpdates');
     assert.strictEqual(GrReviewerUpdatesParser.parse(change), change);
     assert.isFalse(
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.js b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.js
index f0266fd..5e20717 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.js
@@ -22,22 +22,16 @@
 
 suite('gr-shell-command tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
     element.text = `git fetch http://gerrit@localhost:8080/a/test-project
         refs/changes/05/5/1 && git checkout FETCH_HEAD`;
     flushAsynchronousOperations();
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('focusOnCopy', () => {
-    const focusStub = sandbox.stub(element.shadowRoot
+    const focusStub = sinon.stub(element.shadowRoot
         .querySelector('gr-copy-clipboard'),
     'focusOnCopy');
     element.focusOnCopy();
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.js b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.js
index 94691d5..99f953f 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.js
@@ -22,7 +22,6 @@
 
 suite('gr-storage tests', () => {
   let element;
-  let sandbox;
 
   function mockStorage(opt_quotaExceeded) {
     return {
@@ -38,12 +37,10 @@
 
   setup(() => {
     element = basicFixture.instantiate();
-    sandbox = sinon.sandbox.create();
+
     element._storage = mockStorage();
   });
 
-  teardown(() => sandbox.restore());
-
   test('storing, retrieving and erasing drafts', () => {
     const changeNum = 1234;
     const patchNum = 5;
@@ -93,7 +90,7 @@
     // Make sure that the call to cleanup doesn't get throttled.
     element._lastCleanup = 0;
 
-    const cleanupSpy = sandbox.spy(element, '_cleanupItems');
+    const cleanupSpy = sinon.spy(element, '_cleanupItems');
 
     // Create a message with a timestamp that is a second behind the max age.
     element._storage.setItem(key, JSON.stringify({
@@ -153,7 +150,7 @@
   });
 
   test('editable content items', () => {
-    const cleanupStub = sandbox.stub(element, '_cleanupItems');
+    const cleanupStub = sinon.stub(element, '_cleanupItems');
     const key = 'testKey';
     const computedKey = element._getEditableContentKey(key);
     // Key correctly computed.
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.js
index 693da27..2aa7697 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.js
@@ -31,16 +31,10 @@
 
 suite('gr-textarea tests', () => {
   let element;
-  let sandbox;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     element = basicFixture.instantiate();
-    sandbox.stub(element.reporting, 'reportInteraction');
-  });
-
-  teardown(() => {
-    sandbox.restore();
+    sinon.stub(element.reporting, 'reportInteraction');
   });
 
   test('monospace is set properly', () => {
@@ -137,7 +131,7 @@
         // Since selectionStart is on Chrome set always on end of text, we
         // stub it to 1
         const text = ': hello';
-        sandbox.stub(element.$, 'textarea', {
+        sinon.stub(element.$, 'textarea').value( {
           selectionStart: 1,
           value: text,
           textarea: {
@@ -152,7 +146,7 @@
         assert.equal(element._currentSearchString, '');
       });
   test('emoji selector closes when text changes before the colon', () => {
-    const resetStub = sandbox.stub(element, '_resetEmojiDropdown');
+    const resetStub = sinon.stub(element, '_resetEmojiDropdown');
     MockInteractions.focus(element.$.textarea);
     flushAsynchronousOperations();
     element.$.textarea.selectionStart = 10;
@@ -172,7 +166,7 @@
   });
 
   test('_resetEmojiDropdown', () => {
-    const closeSpy = sandbox.spy(element, 'closeDropdown');
+    const closeSpy = sinon.spy(element, 'closeDropdown');
     element._resetEmojiDropdown();
     assert.equal(element._currentSearchString, '');
     assert.isTrue(element._hideAutocomplete);
@@ -186,7 +180,7 @@
 
   test('_determineSuggestions', () => {
     const emojiText = 'tear';
-    const formatSpy = sandbox.spy(element, '_formatSuggestions');
+    const formatSpy = sinon.spy(element, '_formatSuggestions');
     element._determineSuggestions(emojiText);
     assert.isTrue(formatSpy.called);
     assert.isTrue(formatSpy.lastCall.calledWithExactly(
@@ -227,7 +221,7 @@
   });
 
   test('emoji dropdown is closed when iron-overlay-closed is fired', () => {
-    const resetSpy = sandbox.spy(element, '_resetEmojiDropdown');
+    const resetSpy = sinon.spy(element, '_resetEmojiDropdown');
     element.$.emojiSuggestions.dispatchEvent(
         new CustomEvent('dropdown-closed', {
           composed: true, bubbles: true,
@@ -256,7 +250,7 @@
     }
 
     test('escape key', () => {
-      const resetSpy = sandbox.spy(element, '_resetEmojiDropdown');
+      const resetSpy = sinon.spy(element, '_resetEmojiDropdown');
       MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 27);
       assert.isFalse(resetSpy.called);
       setupDropdown();
@@ -266,7 +260,7 @@
     });
 
     test('up key', () => {
-      const upSpy = sandbox.spy(element.$.emojiSuggestions, 'cursorUp');
+      const upSpy = sinon.spy(element.$.emojiSuggestions, 'cursorUp');
       MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 38);
       assert.isFalse(upSpy.called);
       setupDropdown();
@@ -275,7 +269,7 @@
     });
 
     test('down key', () => {
-      const downSpy = sandbox.spy(element.$.emojiSuggestions, 'cursorDown');
+      const downSpy = sinon.spy(element.$.emojiSuggestions, 'cursorDown');
       MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 40);
       assert.isFalse(downSpy.called);
       setupDropdown();
@@ -284,7 +278,7 @@
     });
 
     test('enter key', () => {
-      const enterSpy = sandbox.spy(element.$.emojiSuggestions,
+      const enterSpy = sinon.spy(element.$.emojiSuggestions,
           'getCursorTarget');
       MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
       assert.isFalse(enterSpy.called);
@@ -296,7 +290,7 @@
     });
 
     test('enter key - ignored on just colon without more information', () => {
-      const enterSpy = sandbox.spy(element.$.emojiSuggestions,
+      const enterSpy = sinon.spy(element.$.emojiSuggestions,
           'getCursorTarget');
       MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
       assert.isFalse(enterSpy.called);
@@ -318,17 +312,11 @@
   // properties before ready() is called.
 
     let element;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       element = monospaceFixture.instantiate();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('monospace is set properly', () => {
       assert.isTrue(element.classList.contains('monospace'));
     });
@@ -342,17 +330,11 @@
   // properties before ready() is called.
 
     let element;
-    let sandbox;
 
     setup(() => {
-      sandbox = sinon.sandbox.create();
       element = hideBorderFixture.instantiate();
     });
 
-    teardown(() => {
-      sandbox.restore();
-    });
-
     test('hideBorder is set properly', () => {
       assert.isTrue(element.$.textarea.classList.contains('noBorder'));
     });
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.js b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.js
index b0868a8..7c40b7a 100644
--- a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.js
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.js
@@ -25,7 +25,6 @@
 `);
 
 suite('GrEmailSuggestionsProvider tests', () => {
-  let sandbox;
   let restAPI;
   let provider;
   const account1 = {
@@ -38,8 +37,6 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
     });
@@ -47,13 +44,9 @@
     provider = new GrEmailSuggestionsProvider(restAPI);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('getSuggestions', done => {
     const getSuggestedAccountsStub =
-        sandbox.stub(restAPI, 'getSuggestedAccounts')
+        sinon.stub(restAPI, 'getSuggestedAccounts')
             .returns(Promise.resolve([account1, account2]));
 
     provider.getSuggestions('Some input').then(res => {
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.js b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.js
index dda9f7d..0939f76 100644
--- a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.js
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.js
@@ -25,7 +25,6 @@
 `);
 
 suite('GrGroupSuggestionsProvider tests', () => {
-  let sandbox;
   let restAPI;
   let provider;
   const group1 = {
@@ -39,8 +38,6 @@
   };
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
-
     stub('gr-rest-api-interface', {
       getConfig() { return Promise.resolve({}); },
     });
@@ -48,13 +45,9 @@
     provider = new GrGroupSuggestionsProvider(restAPI);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
-
   test('getSuggestions', done => {
     const getSuggestedAccountsStub =
-        sandbox.stub(restAPI, 'getSuggestedGroups')
+        sinon.stub(restAPI, 'getSuggestedGroups')
             .returns(Promise.resolve({
               'Some name': {id: 1},
               'Other name': {id: 3, url: 'abcd'},
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.js b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.js
index d3eed6b..fba4b26 100644
--- a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.js
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.js
@@ -26,7 +26,6 @@
 `);
 
 suite('GrReviewerSuggestionsProvider tests', () => {
-  let sandbox;
   let _nextAccountId = 0;
   const makeAccount = function(opt_status) {
     const accountId = ++_nextAccountId;
@@ -88,13 +87,10 @@
         REVIEWER: [existingReviewer2],
       },
     };
-    sandbox = sinon.sandbox.create();
+
     return flush(done);
   });
 
-  teardown(() => {
-    sandbox.restore();
-  });
   suite('allowAnyUser set to false', () => {
     setup(done => {
       provider = GrReviewerSuggestionsProvider.create(restAPI, change._number,
@@ -168,7 +164,7 @@
           value: {account, count: 1},
         });
 
-        sandbox.stub(GrDisplayNameUtils, '_accountEmail',
+        sinon.stub(GrDisplayNameUtils, '_accountEmail').callsFake(
             () => '');
 
         suggestion = provider.makeSuggestionItem(account3);
@@ -207,10 +203,10 @@
 
     test('getChangeSuggestedReviewers is used', done => {
       const suggestReviewerStub =
-          sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
+          sinon.stub(restAPI, 'getChangeSuggestedReviewers')
               .returns(Promise.resolve([]));
       const suggestAccountStub =
-          sandbox.stub(restAPI, 'getSuggestedAccounts')
+          sinon.stub(restAPI, 'getSuggestedAccounts')
               .returns(Promise.resolve([]));
 
       provider.getSuggestions('').then(() => {
@@ -231,10 +227,10 @@
 
     test('getSuggestedAccounts is used', done => {
       const suggestReviewerStub =
-          sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
+          sinon.stub(restAPI, 'getChangeSuggestedReviewers')
               .returns(Promise.resolve([]));
       const suggestAccountStub =
-          sandbox.stub(restAPI, 'getSuggestedAccounts')
+          sinon.stub(restAPI, 'getSuggestedAccounts')
               .returns(Promise.resolve([]));
 
       provider.getSuggestions('').then(() => {
diff --git a/polygerrit-ui/app/services/gr-event-interface/gr-event-interface_test.js b/polygerrit-ui/app/services/gr-event-interface/gr-event-interface_test.js
index 4830b17..1cdd6e3 100644
--- a/polygerrit-ui/app/services/gr-event-interface/gr-event-interface_test.js
+++ b/polygerrit-ui/app/services/gr-event-interface/gr-event-interface_test.js
@@ -25,14 +25,8 @@
 const pluginApi = _testOnly_initGerritPluginApi();
 
 suite('gr-event-interface tests', () => {
-  let sandbox;
-
   setup(() => {
-    sandbox = sinon.sandbox.create();
-  });
 
-  teardown(() => {
-    sandbox.restore();
   });
 
   suite('test on Gerrit', () => {
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js b/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js
index bd19c0b..01ba3cb 100644
--- a/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js
@@ -20,22 +20,20 @@
 import {appContext} from '../app-context.js';
 suite('gr-reporting tests', () => {
   let service;
-  let sandbox;
+
   let clock;
   let fakePerformance;
 
   const NOW_TIME = 100;
 
   setup(() => {
-    sandbox = sinon.sandbox.create();
     clock = sinon.useFakeTimers(NOW_TIME);
     service = new GrReporting(appContext.flagsService);
     service._baselines = Object.assign({}, DEFAULT_STARTUP_TIMERS);
-    sandbox.stub(service, 'reporter');
+    sinon.stub(service, 'reporter');
   });
 
   teardown(() => {
-    sandbox.restore();
     clock.restore();
   });
 
@@ -45,9 +43,8 @@
       loadEventEnd: 2,
     };
     fakePerformance.toJSON = () => fakePerformance;
-    sinon.stub(service, 'performanceTiming',
-        {get() { return fakePerformance; }});
-    sandbox.stub(window.performance, 'now').returns(42);
+    sinon.stub(service, 'performanceTiming').get(() => fakePerformance);
+    sinon.stub(window.performance, 'now').returns(42);
     service.appStarted();
     assert.isTrue(
         service.reporter.calledWithMatch(
@@ -62,7 +59,7 @@
   });
 
   test('WebComponentsReady', () => {
-    sandbox.stub(window.performance, 'now').returns(42);
+    sinon.stub(window.performance, 'now').returns(42);
     service.timeEnd('WebComponentsReady');
     assert.isTrue(service.reporter.calledWithMatch(
         'timing-report', 'UI Latency', 'WebComponentsReady', 42
@@ -71,7 +68,7 @@
 
   test('beforeLocationChanged', () => {
     service._baselines['garbage'] = 'monster';
-    sandbox.stub(service, 'time');
+    sinon.stub(service, 'time');
     service.beforeLocationChanged();
     assert.isTrue(service.time.calledWithExactly('DashboardDisplayed'));
     assert.isTrue(service.time.calledWithExactly('ChangeDisplayed'));
@@ -82,7 +79,7 @@
   });
 
   test('changeDisplayed', () => {
-    sandbox.spy(service, 'timeEnd');
+    sinon.spy(service, 'timeEnd');
     service.changeDisplayed();
     assert.isFalse(service.timeEnd.calledWith('ChangeDisplayed'));
     assert.isTrue(service.timeEnd.calledWith('StartupChangeDisplayed'));
@@ -91,7 +88,7 @@
   });
 
   test('changeFullyLoaded', () => {
-    sandbox.spy(service, 'timeEnd');
+    sinon.spy(service, 'timeEnd');
     service.changeFullyLoaded();
     assert.isFalse(
         service.timeEnd.calledWithExactly('ChangeFullyLoaded'));
@@ -102,7 +99,7 @@
   });
 
   test('diffViewDisplayed', () => {
-    sandbox.spy(service, 'timeEnd');
+    sinon.spy(service, 'timeEnd');
     service.diffViewDisplayed();
     assert.isFalse(service.timeEnd.calledWith('DiffViewDisplayed'));
     assert.isTrue(service.timeEnd.calledWith('StartupDiffViewDisplayed'));
@@ -111,7 +108,7 @@
   });
 
   test('fileListDisplayed', () => {
-    sandbox.spy(service, 'timeEnd');
+    sinon.spy(service, 'timeEnd');
     service.fileListDisplayed();
     assert.isFalse(
         service.timeEnd.calledWithExactly('FileListDisplayed'));
@@ -122,7 +119,7 @@
   });
 
   test('dashboardDisplayed', () => {
-    sandbox.spy(service, 'timeEnd');
+    sinon.spy(service, 'timeEnd');
     service.dashboardDisplayed();
     assert.isFalse(service.timeEnd.calledWith('DashboardDisplayed'));
     assert.isTrue(service.timeEnd.calledWith('StartupDashboardDisplayed'));
@@ -131,8 +128,8 @@
   });
 
   test('dashboardDisplayed details', () => {
-    sandbox.spy(service, 'timeEnd');
-    sandbox.stub(window, 'performance', {
+    sinon.spy(service, 'timeEnd');
+    sinon.stub(window, 'performance').value( {
       memory: {
         usedJSHeapSize: 1024 * 1024,
       },
@@ -175,8 +172,8 @@
     };
 
     setup(() => {
-      sandbox.spy(service, 'timeEnd');
-      nowStub = sandbox.stub(window.performance, 'now');
+      sinon.spy(service, 'timeEnd');
+      nowStub = sinon.stub(window.performance, 'now');
       visibilityStateStub = {
         value: value => {
           Object.defineProperty(document, 'visibilityState',
@@ -228,7 +225,7 @@
       visibilityStateStub.value('visible');
       nowStub.returns(15);
       service.beforeLocationChanged();
-      service.timeEnd.reset();
+      service.timeEnd.resetHistory();
       service.dashboardDisplayed();
       assert.isTrue(
           service.timeEnd.calledWithMatch('DashboardDisplayed',
@@ -238,7 +235,7 @@
   });
 
   test('time and timeEnd', () => {
-    const nowStub = sandbox.stub(window.performance, 'now').returns(0);
+    const nowStub = sinon.stub(window.performance, 'now').returns(0);
     service.time('foo');
     nowStub.returns(1);
     service.time('bar');
@@ -255,7 +252,7 @@
   });
 
   test('timer object', () => {
-    const nowStub = sandbox.stub(window.performance, 'now').returns(100);
+    const nowStub = sinon.stub(window.performance, 'now').returns(100);
     const timer = service.getTimer('foo-bar');
     nowStub.returns(150);
     timer.end();
@@ -273,7 +270,7 @@
   });
 
   test('timer object maximum', () => {
-    const nowStub = sandbox.stub(window.performance, 'now').returns(100);
+    const nowStub = sinon.stub(window.performance, 'now').returns(100);
     const timer = service.getTimer('foo-bar').withMaximum(100);
     nowStub.returns(150);
     timer.end();
@@ -287,8 +284,8 @@
 
   test('recordDraftInteraction', () => {
     const key = 'TimeBetweenDraftActions';
-    const nowStub = sandbox.stub(window.performance, 'now').returns(100);
-    const timingStub = sandbox.stub(service, '_reportTiming');
+    const nowStub = sinon.stub(window.performance, 'now').returns(100);
+    const timingStub = sinon.stub(service, '_reportTiming');
     service.recordDraftInteraction();
     assert.isFalse(timingStub.called);
 
@@ -310,7 +307,7 @@
   });
 
   test('timeEndWithAverage', () => {
-    const nowStub = sandbox.stub(window.performance, 'now').returns(0);
+    const nowStub = sinon.stub(window.performance, 'now').returns(0);
     nowStub.returns(1000);
     service.time('foo');
     nowStub.returns(1100);
@@ -331,7 +328,7 @@
 
   test('reportInteraction', () => {
     service.reporter.restore();
-    sandbox.spy(service, '_reportEvent');
+    sinon.spy(service, '_reportEvent');
     service.pluginsLoaded(); // so we don't cache
     service.reportInteraction('button-click', {name: 'sendReply'});
     assert.isTrue(service._reportEvent.getCall(2).calledWithMatch(
@@ -345,9 +342,9 @@
 
   test('report start time', () => {
     service.reporter.restore();
-    sandbox.stub(window.performance, 'now').returns(42);
-    sandbox.spy(service, '_reportEvent');
-    const dispatchStub = sandbox.spy(document, 'dispatchEvent');
+    sinon.stub(window.performance, 'now').returns(42);
+    sinon.spy(service, '_reportEvent');
+    const dispatchStub = sinon.spy(document, 'dispatchEvent');
     service.pluginsLoaded();
     service.time('timeAction');
     service.timeEnd('timeAction');
@@ -366,11 +363,11 @@
   suite('plugins', () => {
     setup(() => {
       service.reporter.restore();
-      sandbox.stub(service, '_reportEvent');
+      sinon.stub(service, '_reportEvent');
     });
 
     test('pluginsLoaded reports time', () => {
-      sandbox.stub(window.performance, 'now').returns(42);
+      sinon.stub(window.performance, 'now').returns(42);
       service.pluginsLoaded();
       assert.isTrue(service._reportEvent.calledWithMatch(
           {
@@ -452,7 +449,7 @@
           this.handlers[type] = handler;
         },
       };
-      sandbox.stub(console, 'error');
+      sinon.stub(console, 'error');
       Object.defineProperty(appContext, 'reportingService', {
         get() {
           return service;
diff --git a/polygerrit-ui/app/test/common-test-setup-karma.js b/polygerrit-ui/app/test/common-test-setup-karma.js
index b09c960..cc934fc 100644
--- a/polygerrit-ui/app/test/common-test-setup-karma.js
+++ b/polygerrit-ui/app/test/common-test-setup-karma.js
@@ -20,6 +20,38 @@
 self.assert = window.chai.assert;
 self.expect = window.chai.expect;
 
+window.addEventListener('error', e => {
+  // For uncaught error mochajs doesn't print the full stack trace.
+  // We should print it ourselves.
+  console.error(e.error.stack.toString());
+});
+
+let originalOnBeforeUnload;
+
+suiteSetup(() => {
+  // This suiteSetup() method is called only once before all tests
+
+  // Can't use window.addEventListener("beforeunload",...) here,
+  // the handler is raised too late.
+  originalOnBeforeUnload = window.onbeforeunload;
+  window.onbeforeunload = e => {
+    // If a test reloads a page, we can't prevent it.
+    // However we can print earror and the stack trace with assert.fail
+    try {
+      throw new Error();
+    } catch (e) {
+      console.error('Page reloading attempt detected.');
+      console.error(e.stack.toString());
+    }
+    originalOnBeforeUnload(e);
+  };
+});
+
+suiteTeardown(() => {
+  // This suiteTeardown() method is called only once after all tests
+  window.onbeforeunload = originalOnBeforeUnload;
+});
+
 // Tests can use fake timers (sandbox.useFakeTimers)
 // Keep the original one for use in test utils methods.
 const nativeSetTimeout = window.setTimeout;
diff --git a/polygerrit-ui/app/test/common-test-setup.js b/polygerrit-ui/app/test/common-test-setup.js
index 2ab9f05..36e27ba 100644
--- a/polygerrit-ui/app/test/common-test-setup.js
+++ b/polygerrit-ui/app/test/common-test-setup.js
@@ -28,6 +28,9 @@
 import {_testOnlyResetGrRestApiSharedObjects} from '../elements/shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import {TestKeyboardShortcutBinder} from './test-utils';
 import {flushDebouncers} from '@polymer/polymer/lib/utils/debounce';
+import {_testOnly_getShortcutManagerInstance} from '../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
+import sinon from 'sinon/pkg/sinon-esm.js';
+window.sinon = sinon;
 
 security.polymer_resin.install({
   allowedIdentifierPrefixes: [''],
@@ -62,6 +65,9 @@
   // The following calls is nessecary to avoid influence of previously executed
   // tests.
   TestKeyboardShortcutBinder.push();
+  const mgr = _testOnly_getShortcutManagerInstance();
+  assert.equal(mgr.activeHosts.size, 0);
+  assert.equal(mgr.listeners.size, 0);
   document.getSelection().removeAllRanges();
   const pl = _testOnly_resetPluginLoader();
   // For testing, always init with empty plugin list
@@ -82,7 +88,7 @@
   // This method is inspired by web-component-tester method
   const proto = document.createElement(tagName).constructor.prototype;
   const stubs = Object.keys(implementation)
-      .map(key => sinon.stub(proto, key, implementation[key]));
+      .map(key => sinon.stub(proto, key).callsFake(implementation[key]));
   cleanups.push(() => {
     stubs.forEach(stub => {
       stub.restore();
@@ -129,6 +135,7 @@
 }
 
 teardown(() => {
+  sinon.restore();
   cleanups.forEach(cleanup => cleanup());
   cleanups.splice(0);
   TestKeyboardShortcutBinder.pop();
diff --git a/polygerrit-ui/app/test/mocks/comment-api.js b/polygerrit-ui/app/test/mocks/comment-api.js
index 631f4a4..f2ca48c 100644
--- a/polygerrit-ui/app/test/mocks/comment-api.js
+++ b/polygerrit-ui/app/test/mocks/comment-api.js
@@ -26,7 +26,6 @@
 class CommentApiMock extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
-
   static get properties() {
     return {
       _changeComments: Object,
diff --git a/polygerrit-ui/package.json b/polygerrit-ui/package.json
index b7077a6..ea039d5 100644
--- a/polygerrit-ui/package.json
+++ b/polygerrit-ui/package.json
@@ -14,7 +14,8 @@
     "karma-mocha": "^2.0.1",
     "karma-mocha-reporter": "^2.2.5",
     "lodash": "^4.17.15",
-    "mocha": "^7.1.1"
+    "mocha": "^7.1.1",
+    "sinon": "^9.0.2"
   },
   "license": "Apache-2.0",
   "private": true
diff --git a/polygerrit-ui/yarn.lock b/polygerrit-ui/yarn.lock
index 38a0c71..d73e1d0 100644
--- a/polygerrit-ui/yarn.lock
+++ b/polygerrit-ui/yarn.lock
@@ -906,6 +906,42 @@
   dependencies:
     estree-walker "^1.0.1"
 
+"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2":
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
+  integrity sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==
+  dependencies:
+    type-detect "4.0.8"
+
+"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
+  integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
+  dependencies:
+    "@sinonjs/commons" "^1.7.0"
+
+"@sinonjs/formatio@^5.0.1":
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089"
+  integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==
+  dependencies:
+    "@sinonjs/commons" "^1"
+    "@sinonjs/samsam" "^5.0.2"
+
+"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3":
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938"
+  integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==
+  dependencies:
+    "@sinonjs/commons" "^1.6.0"
+    lodash.get "^4.4.2"
+    type-detect "^4.0.8"
+
+"@sinonjs/text-encoding@^0.7.1":
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
+  integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
+
 "@types/minimatch@^3.0.3":
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@@ -1614,6 +1650,11 @@
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
   integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
 
+diff@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+  integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
 dom-serialize@^2.2.0:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
@@ -2076,6 +2117,11 @@
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
 has-symbols@^1.0.0, has-symbols@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
@@ -2299,6 +2345,11 @@
   resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
   integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
 
+isarray@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+  integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
+
 isarray@2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
@@ -2416,6 +2467,11 @@
     json-schema "0.2.3"
     verror "1.10.0"
 
+just-extend@^4.0.2:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4"
+  integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==
+
 karma-chrome-launcher@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738"
@@ -2612,6 +2668,11 @@
   resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
   integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
 
+lodash.get@^4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+  integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
+
 lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -2801,6 +2862,17 @@
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
   integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
 
+nise@^4.0.1:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd"
+  integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==
+  dependencies:
+    "@sinonjs/commons" "^1.7.0"
+    "@sinonjs/fake-timers" "^6.0.0"
+    "@sinonjs/text-encoding" "^0.7.1"
+    just-extend "^4.0.2"
+    path-to-regexp "^1.7.0"
+
 no-case@^2.2.0:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
@@ -3020,6 +3092,13 @@
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
   integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
 
+path-to-regexp@^1.7.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
+  integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
+  dependencies:
+    isarray "0.0.1"
+
 path-type@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@@ -3396,6 +3475,19 @@
   resolved "https://registry.yarnpkg.com/shady-css-scoped-element/-/shady-css-scoped-element-0.0.2.tgz#c538fcfe2317e979cd02dfec533898b95b4ea8fe"
   integrity sha512-Dqfl70x6JiwYDujd33ZTbtCK0t52E7+H2swdWQNSTzfsolSa6LJHnTpN4T9OpJJEq4bxuzHRLFO9RBcy/UfrMQ==
 
+sinon@^9.0.2:
+  version "9.0.2"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.2.tgz#b9017e24633f4b1c98dfb6e784a5f0509f5fd85d"
+  integrity sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==
+  dependencies:
+    "@sinonjs/commons" "^1.7.2"
+    "@sinonjs/fake-timers" "^6.0.1"
+    "@sinonjs/formatio" "^5.0.1"
+    "@sinonjs/samsam" "^5.0.3"
+    diff "^4.0.2"
+    nise "^4.0.1"
+    supports-color "^7.1.0"
+
 socket.io-adapter@~1.1.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9"
@@ -3593,6 +3685,13 @@
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
+  integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
+  dependencies:
+    has-flag "^4.0.0"
+
 systemjs@^4.0.0:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-4.1.1.tgz#c90061456f9707478d487b47f3b92b9896032889"
@@ -3702,7 +3801,7 @@
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
   integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
-type-detect@^4.0.0, type-detect@^4.0.5:
+type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
   integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==