Files
gerrit/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
Kasper Nilsson 7db7304a5b Add restore action to edit controls
This change adds the restore action, dialog, and button to
gr-edit-controls. By default, the button itself is hidden -- it is
intended to only be functional with a path. As opposed to the dialog
taking a file path as user input, it functions as a confirm dialog for
the provided file.

An <input> element is used in the restore dialog exclusively for visual
alignment with the rest of the dialogs.

TODO:
- Implement per-file actions for use of the restore dialog, where the
  public `openRestoreDialog` will be called with a file path.
- Make the _path property of gr-edit-controls public to allow a default
  value.
- Add gr-edit-controls to the diff-view and editor-view, which both need
  the restore button shown.

Bug: Issue 4437
Change-Id: Ideb2f9df30a3a0393c031ba9ad01264c930ac467
2017-10-13 14:47:14 -07:00

350 lines
12 KiB
HTML

<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-edit-controls</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-edit-controls.html">
<script>void(0);</script>
<test-fixture id="basic">
<template>
<gr-edit-controls></gr-edit-controls>
</template>
</test-fixture>
<script>
suite('gr-edit-controls tests', () => {
let element;
let sandbox;
let showDialogSpy;
let closeDialogSpy;
let queryStub;
setup(() => {
sandbox = sinon.sandbox.create();
element = fixture('basic');
element.change = {_number: '42'};
showDialogSpy = sandbox.spy(element, '_showDialog');
closeDialogSpy = sandbox.spy(element, '_closeDialog');
queryStub = sandbox.stub(element.$.restAPI, 'queryChangeFiles')
.returns(Promise.resolve([]));
flushAsynchronousOperations();
});
teardown(() => { sandbox.restore(); });
test('all actions exist', () => {
assert.equal(Polymer.dom(element.root).querySelectorAll('gr-button').length,
element._actions.length);
});
suite('edit button CUJ', () => {
let navStubs;
setup(() => {
navStubs = [
sandbox.stub(Gerrit.Nav, 'getEditUrlForDiff'),
sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl'),
];
});
test('_isValidPath', () => {
assert.isFalse(element._isValidPath(''));
assert.isFalse(element._isValidPath('test/'));
assert.isFalse(element._isValidPath('/'));
assert.isTrue(element._isValidPath('test/path.cpp'));
assert.isTrue(element._isValidPath('test.js'));
});
test('edit', () => {
MockInteractions.tap(element.$$('#edit'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.editDialog.disabled);
assert.isFalse(queryStub.called);
element.$.editDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isFalse(element.$.editDialog.disabled);
MockInteractions.tap(element.$.editDialog.$$('gr-button[primary]'));
for (const stub of navStubs) { assert.isTrue(stub.called); }
assert.isTrue(closeDialogSpy.called);
});
});
test('cancel', () => {
MockInteractions.tap(element.$$('#edit'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.editDialog.disabled);
element.$.editDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isFalse(element.$.editDialog.disabled);
MockInteractions.tap(element.$.editDialog.$$('gr-button'));
for (const stub of navStubs) { assert.isFalse(stub.called); }
assert.isTrue(closeDialogSpy.called);
assert.equal(element._path, 'src/test.cpp');
});
});
});
suite('delete button CUJ', () => {
let navStub;
let deleteStub;
setup(() => {
navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
deleteStub = sandbox.stub(element.$.restAPI, 'deleteFileInChangeEdit');
});
test('delete', () => {
deleteStub.returns(Promise.resolve({ok: true}));
MockInteractions.tap(element.$$('#delete'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.deleteDialog.disabled);
assert.isFalse(queryStub.called);
element.$.deleteDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isFalse(element.$.deleteDialog.disabled);
MockInteractions.tap(element.$.deleteDialog.$$('gr-button[primary]'));
flushAsynchronousOperations();
assert.isTrue(deleteStub.called);
return deleteStub.lastCall.returnValue.then(() => {
assert.equal(element._path, '');
assert.isTrue(navStub.called);
assert.isTrue(closeDialogSpy.called);
});
});
});
test('delete fails', () => {
deleteStub.returns(Promise.resolve({ok: false}));
MockInteractions.tap(element.$$('#delete'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.deleteDialog.disabled);
assert.isFalse(queryStub.called);
element.$.deleteDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isFalse(element.$.deleteDialog.disabled);
MockInteractions.tap(element.$.deleteDialog.$$('gr-button[primary]'));
flushAsynchronousOperations();
assert.isTrue(deleteStub.called);
return deleteStub.lastCall.returnValue.then(() => {
assert.isFalse(navStub.called);
assert.isFalse(closeDialogSpy.called);
});
});
});
test('cancel', () => {
MockInteractions.tap(element.$$('#delete'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.deleteDialog.disabled);
element.$.deleteDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isFalse(element.$.deleteDialog.disabled);
MockInteractions.tap(element.$.deleteDialog.$$('gr-button'));
assert.isFalse(navStub.called);
assert.isTrue(closeDialogSpy.called);
assert.equal(element._path, 'src/test.cpp');
});
});
});
suite('rename button CUJ', () => {
let navStub;
let renameStub;
setup(() => {
navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
renameStub = sandbox.stub(element.$.restAPI, 'renameFileInChangeEdit');
});
test('rename', () => {
renameStub.returns(Promise.resolve({ok: true}));
MockInteractions.tap(element.$$('#rename'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.renameDialog.disabled);
assert.isFalse(queryStub.called);
element.$.renameDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isTrue(element.$.renameDialog.disabled);
element.$.renameDialog.querySelector('.newPathInput').bindValue =
'src/test.newPath';
assert.isFalse(element.$.renameDialog.disabled);
MockInteractions.tap(element.$.renameDialog.$$('gr-button[primary]'));
flushAsynchronousOperations();
assert.isTrue(renameStub.called);
return renameStub.lastCall.returnValue.then(() => {
assert.equal(element._path, '');
assert.isTrue(navStub.called);
assert.isTrue(closeDialogSpy.called);
});
});
});
test('rename fails', () => {
renameStub.returns(Promise.resolve({ok: false}));
MockInteractions.tap(element.$$('#rename'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.renameDialog.disabled);
assert.isFalse(queryStub.called);
element.$.renameDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isTrue(element.$.renameDialog.disabled);
element.$.renameDialog.querySelector('.newPathInput').bindValue =
'src/test.newPath';
assert.isFalse(element.$.renameDialog.disabled);
MockInteractions.tap(element.$.renameDialog.$$('gr-button[primary]'));
flushAsynchronousOperations();
assert.isTrue(renameStub.called);
return renameStub.lastCall.returnValue.then(() => {
assert.isFalse(navStub.called);
assert.isFalse(closeDialogSpy.called);
});
});
});
test('cancel', () => {
MockInteractions.tap(element.$$('#rename'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.renameDialog.disabled);
element.$.renameDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
element.$.renameDialog.querySelector('.newPathInput').bindValue =
'src/test.newPath';
assert.isFalse(element.$.renameDialog.disabled);
MockInteractions.tap(element.$.renameDialog.$$('gr-button'));
assert.isFalse(navStub.called);
assert.isTrue(closeDialogSpy.called);
assert.equal(element._path, 'src/test.cpp');
assert.equal(element._newPath, 'src/test.newPath');
});
});
});
suite('restore button CUJ', () => {
let navStub;
let restoreStub;
setup(() => {
navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
restoreStub = sandbox.stub(element.$.restAPI, 'restoreFileInChangeEdit');
});
test('restore hidden by default', () => {
assert.isTrue(element.$$('#restore').classList.contains('invisible'));
});
test('restore', () => {
restoreStub.returns(Promise.resolve({ok: true}));
element._path = 'src/test.cpp';
MockInteractions.tap(element.$$('#restore'));
return showDialogSpy.lastCall.returnValue.then(() => {
MockInteractions.tap(element.$.restoreDialog.$$('gr-button[primary]'));
flushAsynchronousOperations();
assert.isTrue(restoreStub.called);
assert.equal(restoreStub.lastCall.args[1], 'src/test.cpp');
return restoreStub.lastCall.returnValue.then(() => {
assert.equal(element._path, '');
assert.isTrue(navStub.called);
assert.isTrue(closeDialogSpy.called);
});
});
});
test('restore fails', () => {
restoreStub.returns(Promise.resolve({ok: false}));
element._path = 'src/test.cpp';
MockInteractions.tap(element.$$('#restore'));
return showDialogSpy.lastCall.returnValue.then(() => {
MockInteractions.tap(element.$.restoreDialog.$$('gr-button[primary]'));
flushAsynchronousOperations();
assert.isTrue(restoreStub.called);
assert.equal(restoreStub.lastCall.args[1], 'src/test.cpp');
return restoreStub.lastCall.returnValue.then(() => {
assert.isFalse(navStub.called);
assert.isFalse(closeDialogSpy.called);
});
});
});
test('cancel', () => {
element._path = 'src/test.cpp';
MockInteractions.tap(element.$$('#restore'));
return showDialogSpy.lastCall.returnValue.then(() => {
MockInteractions.tap(element.$.restoreDialog.$$('gr-button'));
assert.isFalse(navStub.called);
assert.isTrue(closeDialogSpy.called);
assert.equal(element._path, 'src/test.cpp');
});
});
});
test('openEditDialog', () => {
return element.openEditDialog('test/path.cpp').then(() => {
assert.isFalse(element.$.editDialog.hasAttribute('hidden'));
assert.equal(element.$.editDialog.querySelector('gr-autocomplete').text,
'test/path.cpp');
});
});
test('_getDialogFromEvent', () => {
const spy = sandbox.spy(element, '_getDialogFromEvent');
element.addEventListener('tap', element._getDialogFromEvent);
MockInteractions.tap(element.$.editDialog);
flushAsynchronousOperations();
assert.equal(spy.lastCall.returnValue.id, 'editDialog');
MockInteractions.tap(element.$.deleteDialog);
flushAsynchronousOperations();
assert.equal(spy.lastCall.returnValue.id, 'deleteDialog');
MockInteractions.tap(
element.$.deleteDialog.querySelector('gr-autocomplete'));
flushAsynchronousOperations();
assert.equal(spy.lastCall.returnValue.id, 'deleteDialog');
MockInteractions.tap(element);
flushAsynchronousOperations();
assert.notOk(spy.lastCall.returnValue);
});
});
</script>