This is an automated email from the ASF dual-hosted git repository.
acote pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push:
new d3affac Fix flaky Nightwatch tests (#1248)
d3affac is described below
commit d3affaca0310a0cb59c5b5e0cf05d7b9720d58d3
Author: Antonio Maranhao <[email protected]>
AuthorDate: Wed Apr 29 14:06:22 2020 -0400
Fix flaky Nightwatch tests (#1248)
* Fix flaky Nightwatch test
* Update ConfirmModal
* Update CI
Co-authored-by: Alexis Côté <[email protected]>
---
.../components/components/stringeditmodal.js | 17 ++++++++--
app/addons/cors/__tests__/components.test.js | 6 ++++
.../__tests__/doc-editor.components.test.js | 20 +++++++++++
.../doc-editor/components/DocEditorScreen.js | 2 +-
.../documents/tests/nightwatch/deletesDocuments.js | 1 +
.../documents/tests/nightwatch/viewDelete.js | 3 +-
app/addons/fauxton/components.js | 23 +++++++------
.../search/tests/nightwatch/deleteSearchIndex.js | 1 +
bin/docker-up-and-check.sh | 4 +--
docker/dc.selenium-debug.yml | 2 +-
docker/dc.selenium.yml | 3 +-
.../custom-commands/clickWhenVisible.js | 1 -
test/nightwatch_tests/helpers/helpers.js | 39 ++++++++++++++++++++--
test/nightwatch_tests/nightwatch.json.underscore | 2 ++
14 files changed, 99 insertions(+), 25 deletions(-)
diff --git a/app/addons/components/components/stringeditmodal.js
b/app/addons/components/components/stringeditmodal.js
index 0c699be..e0950de 100644
--- a/app/addons/components/components/stringeditmodal.js
+++ b/app/addons/components/components/stringeditmodal.js
@@ -36,6 +36,10 @@ export class StringEditModal extends React.Component {
onSave () { }
};
+ state = {
+ editorInitialized: false
+ };
+
initAceEditor = (dom_node) => {
this.editor = ace.edit(dom_node);
this.editor.$blockScrolling = Infinity; // suppresses an Ace editor error
@@ -44,6 +48,9 @@ export class StringEditModal extends React.Component {
this.editor.setTheme('ace/theme/idle_fingers');
const val = Helpers.parseJSON(this.props.value);
this.editor.setValue(val, -1);
+ if (!this.state.editorInitialized) {
+ this.setState({ editorInitialized: true });
+ }
};
closeModal = () => {
@@ -55,6 +62,12 @@ export class StringEditModal extends React.Component {
};
render() {
+ const { editorInitialized } = this.state;
+ const saveBt = editorInitialized ? (
+ <button id="string-edit-save-btn" onClick={this.save} className="btn
btn-primary save">
+ <i className="fonticon-circle-check"></i> Modify Text
+ </button>
+ ) : null;
return (
<Modal className="string-editor-modal" show={this.props.visible}
onHide={this.closeModal}>
<Modal.Header closeButton={true}>
@@ -68,9 +81,7 @@ export class StringEditModal extends React.Component {
</Modal.Body>
<Modal.Footer>
<a className="cancel-link" onClick={this.closeModal}>Cancel</a>
- <button id="string-edit-save-btn" onClick={this.save} className="btn
btn-primary save">
- <i className="fonticon-circle-check"></i> Modify Text
- </button>
+ { saveBt }
</Modal.Footer>
</Modal>
);
diff --git a/app/addons/cors/__tests__/components.test.js
b/app/addons/cors/__tests__/components.test.js
index 9c5d935..b26368c 100644
--- a/app/addons/cors/__tests__/components.test.js
+++ b/app/addons/cors/__tests__/components.test.js
@@ -39,6 +39,7 @@ describe('CORS Components', () => {
saveCORS={sinon.stub()}
showDeleteDomainConfirmation={sinon.stub()}
fetchAndLoadCORSOptions={sinon.stub()}
+ hideDeleteDomainConfirmation={sinon.stub()}
/>);
wrapper.find('.enable-disable.btn').simulate('click');
@@ -56,6 +57,7 @@ describe('CORS Components', () => {
saveCORS={sinon.stub()}
showDeleteDomainConfirmation={sinon.stub()}
fetchAndLoadCORSOptions={sinon.stub()}
+ hideDeleteDomainConfirmation={sinon.stub()}
/>);
wrapper.find('.enable-disable.btn').simulate('click');
sinon.assert.notCalled(spy);
@@ -72,6 +74,7 @@ describe('CORS Components', () => {
saveCORS={sinon.stub()}
showDeleteDomainConfirmation={sinon.stub()}
fetchAndLoadCORSOptions={sinon.stub()}
+ hideDeleteDomainConfirmation={sinon.stub()}
/>);
wrapper.find('input').at(0).simulate('change', { target: { checked:
true, value: 'all' } });
expect(spy.calledOnce).toBeTruthy();
@@ -88,6 +91,7 @@ describe('CORS Components', () => {
saveCORS={sinon.stub()}
showDeleteDomainConfirmation={sinon.stub()}
fetchAndLoadCORSOptions={sinon.stub()}
+ hideDeleteDomainConfirmation={sinon.stub()}
/>);
wrapper.find('input').at(0).simulate('change', { target: { checked:
true, value: 'all' } });
expect(spy.calledOnce).toBeFalsy();
@@ -102,6 +106,7 @@ describe('CORS Components', () => {
saveCORS={sinon.stub()}
showDeleteDomainConfirmation={sinon.stub()}
fetchAndLoadCORSOptions={sinon.stub()}
+ hideDeleteDomainConfirmation={sinon.stub()}
/>);
expect(wrapper.find('.loading-lines').exists()).toBeTruthy();
@@ -116,6 +121,7 @@ describe('CORS Components', () => {
saveCORS={sinon.stub()}
showDeleteDomainConfirmation={sinon.stub()}
fetchAndLoadCORSOptions={sinon.stub()}
+ hideDeleteDomainConfirmation={sinon.stub()}
/>);
expect(wrapper.find('.loading-lines').exists()).toBe(false);
diff --git
a/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
b/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
index 313bc64..5602ebb 100644
--- a/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
+++ b/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
@@ -25,6 +25,7 @@ import { mount } from 'enzyme';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
+import actiontypes from '../actiontypes';
import docEditorReducer from '../reducers';
import '../../base';
@@ -215,8 +216,25 @@ describe('DocEditorContainer', () => {
combineReducers({ docEditor: docEditorReducer, databases: databasesReducer
}),
applyMiddleware(...middlewares)
);
+ const loadDummyDocAction = {
+ type: actiontypes.DOC_LOADED,
+ options: {
+ doc: {
+ get: () => {},
+ unset: () => {},
+ hasChanged: () => false,
+ }
+ }
+ };
+
+ afterEach(() => {
+ store.dispatch({
+ type: actiontypes.RESET_DOC
+ });
+ });
it('clicking Delete button shows the confirmation modal', () => {
+ store.dispatch(loadDummyDocAction);
const wrapper = mount(
<Provider store={store}>
<DocEditorContainer
@@ -230,6 +248,7 @@ describe('DocEditorContainer', () => {
});
it('clicking Upload button shows the upload dialog', () => {
+ store.dispatch(loadDummyDocAction);
const wrapper = mount(
<Provider store={store}>
<DocEditorContainer
@@ -243,6 +262,7 @@ describe('DocEditorContainer', () => {
});
it('clicking Clone button shows the clone doc dialog', () => {
+ store.dispatch(loadDummyDocAction);
const wrapper = mount(
<Provider store={store}>
<DocEditorContainer
diff --git a/app/addons/documents/doc-editor/components/DocEditorScreen.js
b/app/addons/documents/doc-editor/components/DocEditorScreen.js
index 8edd153..f8f661f 100644
--- a/app/addons/documents/doc-editor/components/DocEditorScreen.js
+++ b/app/addons/documents/doc-editor/components/DocEditorScreen.js
@@ -144,7 +144,7 @@ export default class DocEditorScreen extends
React.Component {
};
getButtonRow = () => {
- if (this.props.isNewDoc) {
+ if (this.props.isNewDoc || this.props.doc === null) {
return false;
}
return (
diff --git a/app/addons/documents/tests/nightwatch/deletesDocuments.js
b/app/addons/documents/tests/nightwatch/deletesDocuments.js
index ec34cba..6d0372b 100644
--- a/app/addons/documents/tests/nightwatch/deletesDocuments.js
+++ b/app/addons/documents/tests/nightwatch/deletesDocuments.js
@@ -151,6 +151,7 @@ module.exports = {
.clickWhenVisible('#doc-editor-actions-panel button[title="Delete"]')
.waitForElementVisible('.confirmation-modal', waitTime, false)
.clickWhenVisible('.confirmation-modal button.btn.btn-primary')
+ .waitForElementNotPresent('.confirmation-modal button.btn-primary',
waitTime, true)
.waitForElementPresent('.jump-to-doc', waitTime, false)
//check raw JSON
diff --git a/app/addons/documents/tests/nightwatch/viewDelete.js
b/app/addons/documents/tests/nightwatch/viewDelete.js
index 335e38e..dcc148f 100644
--- a/app/addons/documents/tests/nightwatch/viewDelete.js
+++ b/app/addons/documents/tests/nightwatch/viewDelete.js
@@ -33,9 +33,8 @@ module.exports = {
.clickWhenVisible('.index-list .active span', waitTime, true)
.clickWhenVisible('.popover-content .fonticon-trash', waitTime, true)
- .waitForElementVisible('.confirmation-modal button.btn-primary',
waitTime, true)
.clickWhenVisible('.confirmation-modal button.btn-primary', waitTime,
true)
-
+ .waitForElementNotPresent('.confirmation-modal button.btn-primary',
waitTime, true)
// now wait for the sidebar to have removed the design doc
.waitForElementNotPresent('#testdesigndoc', waitTime, true)
diff --git a/app/addons/fauxton/components.js b/app/addons/fauxton/components.js
index e33cf73..025d845 100644
--- a/app/addons/fauxton/components.js
+++ b/app/addons/fauxton/components.js
@@ -164,9 +164,7 @@ class ConfirmationModal extends React.Component {
title: 'Please confirm',
text: '',
successButtonLabel: 'Okay',
- buttonClass: 'btn-primary',
- onClose: function () { },
- onSubmit: function () { }
+ buttonClass: 'btn-primary'
};
close = (e) => {
@@ -177,12 +175,19 @@ class ConfirmationModal extends React.Component {
};
render() {
- var content = <p>{this.props.text}</p>;
+ let content = <p>{this.props.text}</p>;
if (!_.isString(this.props.text)) {
content = this.props.text;
}
- var btnClasses = 'btn ' + this.props.buttonClass;
-
+ const btnClasses = 'btn ' + this.props.buttonClass;
+ const closeButton = this.props.onClose ? (
+ <a href="#" data-bypass="true" className="cancel-link"
onClick={this.close}>Cancel</a>
+ ) : null;
+ const submitButton = this.props.onSubmit ? (
+ <button className={btnClasses} onClick={this.props.onSubmit}>
+ <i className="fonticon-ok-circled"></i> {this.props.successButtonLabel}
+ </button>
+ ) : null;
return (
<Modal dialogClassName="confirmation-modal" show={this.props.visible}
onHide={this.close}>
<Modal.Header closeButton={true}>
@@ -192,10 +197,8 @@ class ConfirmationModal extends React.Component {
{content}
</Modal.Body>
<Modal.Footer>
- <a href="#" data-bypass="true" className="cancel-link"
onClick={this.close}>Cancel</a>
- <button className={btnClasses} onClick={this.props.onSubmit}>
- <i className="fonticon-ok-circled"></i>
{this.props.successButtonLabel}
- </button>
+ { closeButton }
+ { submitButton }
</Modal.Footer>
</Modal>
);
diff --git a/app/addons/search/tests/nightwatch/deleteSearchIndex.js
b/app/addons/search/tests/nightwatch/deleteSearchIndex.js
index 3ec44fa..b8e4a03 100644
--- a/app/addons/search/tests/nightwatch/deleteSearchIndex.js
+++ b/app/addons/search/tests/nightwatch/deleteSearchIndex.js
@@ -74,6 +74,7 @@ module.exports = {
.clickWhenVisible('.popover-content .fonticon-trash', waitTime, true)
.waitForElementVisible('div.confirmation-modal', waitTime, false)
.clickWhenVisible('.confirmation-modal button.btn.btn-primary')
+ .waitForElementNotPresent('.confirmation-modal button.btn.btn-primary',
waitTime, true)
// just assert the search indexes section has been removed, but the
design doc still exists
.waitForElementNotPresent('#nav-design-function-testdesigndocindexes',
waitTime, true)
diff --git a/bin/docker-up-and-check.sh b/bin/docker-up-and-check.sh
index 7d69859..27f99c7 100755
--- a/bin/docker-up-and-check.sh
+++ b/bin/docker-up-and-check.sh
@@ -36,7 +36,7 @@ start_containters_and_check() {
echo "=============================="
return 1
fi
- if [[ $logs == *"Developers cluster is set up"* ]]; then
+ if [[ $logs == *"Apache CouchDB has started on"* ]]; then
echo "Docker containers are up"
echo "========================"
docker logs couchdb
@@ -59,4 +59,4 @@ then
fi
stop_containters
-exit 2
\ No newline at end of file
+exit 2
diff --git a/docker/dc.selenium-debug.yml b/docker/dc.selenium-debug.yml
index b819f25..45eac62 100644
--- a/docker/dc.selenium-debug.yml
+++ b/docker/dc.selenium-debug.yml
@@ -5,7 +5,7 @@ services:
file: './dc.selenium.yml'
service: selenium
container_name: selenium
- image: selenium/standalone-chrome-debug:3.11.0
+ image: selenium/standalone-chrome-debug:3.13
ports:
- "4444:4444"
- "5900:5900"
diff --git a/docker/dc.selenium.yml b/docker/dc.selenium.yml
index 9ff1e63..6c2f033 100644
--- a/docker/dc.selenium.yml
+++ b/docker/dc.selenium.yml
@@ -2,10 +2,9 @@ version: '2'
services:
selenium:
container_name: selenium
- image: selenium/standalone-chrome:3.11.0
+ image: selenium/standalone-chrome:3.13
ports:
- "4444:4444"
-
couchdb:
container_name: couchdb
image: ${COUCHDB_IMAGE}
diff --git a/test/nightwatch_tests/custom-commands/clickWhenVisible.js
b/test/nightwatch_tests/custom-commands/clickWhenVisible.js
index 2964e76..052169a 100644
--- a/test/nightwatch_tests/custom-commands/clickWhenVisible.js
+++ b/test/nightwatch_tests/custom-commands/clickWhenVisible.js
@@ -20,7 +20,6 @@ exports.command = function (element, waitTime) {
this
.waitForElementPresent(element, waitTime, false)
- .execute('document.querySelector("' + element + '").scrollIntoView();')
.waitForElementVisible(element, waitTime, false)
.click(element);
diff --git a/test/nightwatch_tests/helpers/helpers.js
b/test/nightwatch_tests/helpers/helpers.js
index 78e91ae..f2391ef 100644
--- a/test/nightwatch_tests/helpers/helpers.js
+++ b/test/nightwatch_tests/helpers/helpers.js
@@ -69,15 +69,48 @@ module.exports = {
},
afterEach: function (browser, done) {
+ // Delete test database
var nano =
module.exports.getNanoInstance(browser.globals.test_settings.db_url),
database = module.exports.testDatabaseName;
console.log('nano cleaning up', database);
- nano.db.destroy(database).catch(err => {
+ var destroyDbProm = nano.db.destroy(database).catch(err => {
if (err && err.message !== 'Database does not exist.') {
- console.log('Error in cleaning up ' + database, err.message);
+ console.warn('Error in cleaning up ' + database, err.message);
}
- }).then(() => {
+ });
+
+ // Prints the browser's console logs in case it's a failure
+ var promGetLog = Promise.resolve();
+ if (browser && browser.sessionId && browser.currentTest &&
browser.currentTest.results) {
+ var res = browser.currentTest.results;
+ if (res.errors > 0 || res.failed > 0) {
+ promGetLog = new Promise((resolve, reject) => {
+ try {
+ browser.getLog('browser', (logEntriesArray) => {
+ // !! IMPORTANT: Ends the session since the Nightwatch settings
have "end_session_on_fail: false"
+ try {
+ browser.end();
+ } catch (e) {}
+ resolve(logEntriesArray);
+ });
+ } catch (err) {
+ reject(err);
+ }
+ }).catch(err => {
+ console.warn('Failed to fetch browser logs', err);
+ }).then(logEntriesArray => {
+ if (logEntriesArray) {
+ console.warn('Browser logs for failed test:');
+ logEntriesArray.forEach(function(log) {
+ console.warn(' [' + log.level + '] ' + ' : ' + log.message);
+ });
+ }
+ });
+ }
+ }
+
+ Promise.all([promGetLog, destroyDbProm]).then(() => {
done();
});
}
diff --git a/test/nightwatch_tests/nightwatch.json.underscore
b/test/nightwatch_tests/nightwatch.json.underscore
index 68cf887..524e0e7 100644
--- a/test/nightwatch_tests/nightwatch.json.underscore
+++ b/test/nightwatch_tests/nightwatch.json.underscore
@@ -26,6 +26,8 @@
"password": "<%- password %>",
"launch_url": "http://<%- fauxton_host %>:<%- fauxton_port %>",
"db_url": "<%- db_protocol %>://<%- fauxton_username %>:<%- password
%>@<%- db_host %>:<%- db_port %>",
+ "end_session_on_fail": false,
+ "skip_testcases_on_fail": true,
"selenium_host" : "127.0.0.1",
"selenium_port" : "<%- selenium_port %>",
"silent" : true,