2
0
mirror of https://github.com/frappe/frappe.git synced 2024-06-12 21:32:26 +00:00

Add UI tests using Cypress (#6562)

* test(UI): Add UI tests using cypress

* test: Add test configuration for travis

* fix: Lock redis version

* fix: Refactor fill_field command

* fix: Rename setup_wizard test to run first

* test: Add setup for dashboard service

* test: Add build matrix for ui test

* test: Add name to each build matrix

* test: Only include ui test for an extra build stage

* fix: Exclude UI test with python 3.6

* test: Test order

* test: Enable developer_mode

* test(login): Check session user and not hash

* test: Refactor assert

* test: Refactor setup wizard test

* test: Remove setup wizard test

* test: Add blank seed database

* test(form): Scroll to top before save

* test: Fix form test

* test: timeout

* test: more wait

* test: Remove specific selector

* test: Remove wait, delay typing

* test: Blur input after typing

* test: Wait for form to get dirty

* test: Add credentials for frappe org

* test: Remove node install step

* style: Fix linting issues

* fix: List view filters

- ToDo: Dont override frappe.route_options if it is already set

* test: Dont reload page before test
This commit is contained in:
Faris Ansari 2018-12-03 09:07:31 +05:30 committed by Rushabh Mehta
parent 8ec0bdc402
commit 83d6659e12
19 changed files with 913 additions and 57 deletions

View File

@ -137,6 +137,12 @@
"JsBarcode": true,
"L": true,
"Chart": true,
"DataTable": true
"DataTable": true,
"Cypress": true,
"cy": true,
"it": true,
"context": true,
"before": true,
"beforeEach": true
}
}

4
.gitignore vendored
View File

@ -185,3 +185,7 @@ typings/
# next.js build output
.next
# cypress
cypress/screenshots
cypress/videos

View File

@ -9,6 +9,7 @@ python:
env:
- DB=mariadb
- DB=postgres
- TEST_TYPE=ui
services:
- mysql
@ -18,11 +19,14 @@ addons:
hosts:
- test_site
- test_site_postgres
- test_site_ui
matrix:
exclude:
- python: 2.7
env: DB=postgres
- python: 3.6
env: TEST_TYPE=ui
install:
- $TRAVIS_BUILD_DIR/.travis/install.sh

View File

@ -19,4 +19,5 @@ sudo pip install -e ~/bench
rm $TRAVIS_BUILD_DIR/.git/shallow
cd ~/ && bench init frappe-bench --python $(which python) --frappe-path $TRAVIS_BUILD_DIR
cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/
cp -r $TRAVIS_BUILD_DIR/test_sites/test_site_postgres ~/frappe-bench/sites/
cp -r $TRAVIS_BUILD_DIR/test_sites/test_site_postgres ~/frappe-bench/sites/
cp -r $TRAVIS_BUILD_DIR/test_sites/test_site_ui ~/frappe-bench/sites/

View File

@ -2,15 +2,29 @@
set -e
setup_mariadb_env() {
mysql -u root -ptravis -e "create database $1"
mysql -u root -ptravis -e "USE mysql; CREATE USER '$1'@'localhost' IDENTIFIED BY '$1'; FLUSH PRIVILEGES; "
mysql -u root -ptravis -e "USE mysql; GRANT ALL PRIVILEGES ON \`$1\`.* TO '$1'@'localhost';"
}
if [[ $DB == 'mariadb' ]]; then
mysql -u root -ptravis -e 'create database test_frappe'
mysql -u root -ptravis -e "USE mysql; CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'; FLUSH PRIVILEGES; "
mysql -u root -ptravis -e "USE mysql; GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';"
setup_mariadb_env 'test_frappe'
bench --site test_site reinstall --yes
bench --site test_site setup-help
bench setup-global-help --root_password travis
bench --site test_site scheduler disable
bench --site test_site run-tests --coverage
elif [[ $TEST_TYPE == 'ui' ]]; then
setup_mariadb_env 'test_site_ui'
bench --site test_site_ui --force restore ./apps/frappe/test_sites/test_site_ui/20181116_225029-test_site_ui-database.sql.gz
bench --site test_site_ui migrate
bench --site test_site_ui setup-help
bench setup-global-help --root_password travis
bench --site test_site_ui scheduler disable
cd apps/frappe && yarn && yarn cypress:run
elif [[ $DB == 'postgres' ]]; then
psql -c "CREATE DATABASE test_frappe;" -U postgres
psql -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe';" -U postgres

4
cypress.json Normal file
View File

@ -0,0 +1,4 @@
{
"baseUrl": "http://test_site_ui:8000",
"projectId": "92odwv"
}

View File

@ -0,0 +1,52 @@
context('Awesome Bar', () => {
before(() => {
cy.login('Administrator', 'qwe');
cy.visit('/desk');
});
beforeEach(() => {
cy.get('.navbar-home').click();
});
it('navigates to modules', () => {
cy.get('#navbar-search')
.type('modules{downarrow}{enter}', { delay: 100 });
cy.location('hash').should('eq', '#modules');
});
it('navigates to doctype list', () => {
cy.get('#navbar-search')
.type('todo{downarrow}{enter}', { delay: 100 });
cy.get('h1').should('contain', 'To Do');
cy.location('hash').should('eq', '#List/ToDo/List');
});
it('find text in doctype list', () => {
cy.get('#navbar-search')
.type('test in todo{downarrow}{enter}', { delay: 100 });
cy.get('h1').should('contain', 'To Do');
cy.get('.toggle-filter')
.should('have.length', 1)
.should('contain', 'ID like %test%');
});
it('navigates to new form', () => {
cy.get('#navbar-search')
.type('new blog post{downarrow}{enter}', { delay: 100 });
cy.get('.title-text:visible').should('have.text', 'New Blog Post 1');
});
it('calculates math expressions', () => {
cy.get('#navbar-search')
.type('55 + 32{downarrow}{enter}', { delay: 100 });
cy.get('.modal-title').should('contain', 'Result');
cy.get('.msgprint').should('contain', '55 + 32 = 87');
});
});

View File

@ -0,0 +1,16 @@
context('Form', () => {
before(() => {
cy.login('Administrator', 'qwe');
cy.visit('/desk');
});
it('create a new form', () => {
cy.visit('/desk#Form/ToDo/New ToDo 1');
cy.fill_field('description', 'this is a test todo', 'Text Editor').blur();
cy.get('.page-title').should('contain', 'Not Saved');
cy.get('.primary-action').click();
cy.visit('/desk#List/ToDo');
cy.location('hash').should('eq', '#List/ToDo/List');
cy.get('.list-row').should('contain', 'this is a test todo');
});
});

View File

@ -0,0 +1,39 @@
context('Login', () => {
beforeEach(() => {
cy.visit('/login');
});
it('greets with login screen', () => {
cy.get('.page-card-head').contains('Sign In');
});
it('validates password', () => {
cy.get('#login_email').type('Administrator');
cy.get('.btn-login').click();
cy.location('pathname').should('eq', '/login');
});
it('validates email', () => {
cy.get('#login_password').type('qwe');
cy.get('.btn-login').click();
cy.location('pathname').should('eq', '/login');
});
it('logs in using correct credentials', () => {
cy.get('#login_email').type('Administrator');
cy.get('#login_password').type('qwe');
cy.get('.btn-login').click();
cy.location('pathname').should('eq', '/desk');
cy.window().its('frappe.session.user').should('eq', 'Administrator');
});
it('shows invalid login if incorrect credentials', () => {
cy.get('#login_email').type('Administrator');
cy.get('#login_password').type('qwer');
cy.get('.btn-login').click();
cy.get('.page-card-head').contains('Invalid Login. Try again.');
cy.location('pathname').should('eq', '/login');
});
});

17
cypress/plugins/index.js Normal file
View File

@ -0,0 +1,17 @@
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
module.exports = () => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
};

View File

@ -0,0 +1,52 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... });
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... });
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... });
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... });
Cypress.Commands.add('login', (email, password) => {
cy.request({
url: '/',
method: 'POST',
body: {
cmd: 'login',
usr: email,
pwd: password
}
});
});
Cypress.Commands.add('fill_field', (fieldname, value, fieldtype='Data') => {
let selector = `.form-control[data-fieldname="${fieldname}"]`;
if (fieldtype === 'Text Editor') {
selector = `[data-fieldname="${fieldname}"] .ql-editor`;
}
cy.get(selector).as('input');
if (fieldtype === 'Select') {
return cy.get('@input').select(value);
} else {
return cy.get('@input').type(value);
}
});

25
cypress/support/index.js Normal file
View File

@ -0,0 +1,25 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.Cookies.defaults({
whitelist: 'sid'
});

12
cypress/tsconfig.json Normal file
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "../node_modules",
"types": [
"cypress"
]
},
"include": [
"**/*.*"
]
}

View File

@ -1,11 +1,12 @@
frappe.listview_settings['ToDo'] = {
onload: function(me) {
frappe.route_options = {
"owner": frappe.session.user,
"status": "Open"
};
if (!frappe.route_options) {
frappe.route_options = {
"owner": frappe.session.user,
"status": "Open"
};
}
me.page.set_title(__("To Do"));
},
hide_name_column: true,
refresh: function(me) {

View File

@ -57,6 +57,20 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.actions_menu_items = this.get_actions_menu_items();
if (this.view_user_settings.filters && this.view_user_settings.filters.length) {
// Priority 1: saved filters
const saved_filters = this.view_user_settings.filters;
this.filters = this.validate_filters(saved_filters);
} else {
// Priority 2: filters in listview_settings
this.filters = (this.settings.filters || []).map(f => {
if (f.length === 3) {
f = [this.doctype, f[0], f[1], f[2]];
}
return f;
});
}
this.patch_refresh_and_load_lib();
}
@ -266,23 +280,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
before_refresh() {
if (frappe.route_options) {
// Priority 1: route filters
this.filters = this.parse_filters_from_route_options();
} else if (this.view_user_settings.filters && this.view_user_settings.filters.length) {
// Priority 2: saved filters
const saved_filters = this.view_user_settings.filters;
this.filters = this.validate_filters(saved_filters);
} else {
// Priority 3: filters in listview_settings
this.filters = (this.settings.filters || []).map(f => {
if (f.length === 3) {
f = [this.doctype, f[0], f[1], f[2]];
}
return f;
});
}
if (this.filters.length) {
return this.filter_area.clear(false)
.then(() => this.filter_area.set(this.filters));
}

View File

@ -3,7 +3,9 @@
"scripts": {
"build": "node rollup/build.js",
"production": "FRAPPE_ENV=production node rollup/build.js",
"watch": "node rollup/watch.js"
"watch": "node rollup/watch.js",
"cypress:run": "cypress run --record --key 14ddd919-b01f-4d5f-b9d1-5af54d34c7f3",
"cypress:open": "cypress open"
},
"repository": {
"type": "git",
@ -38,6 +40,7 @@
"devDependencies": {
"babel-runtime": "^6.26.0",
"chalk": "^2.3.2",
"cypress": "^3.1.1",
"less": "^3.0.4",
"node-sass": "^4.9.0",
"rollup": "^0.65.0",

View File

@ -0,0 +1,14 @@
{
"developer_mode": 1,
"db_name": "test_site_ui",
"db_password": "test_site_ui",
"db_type": "mariadb",
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "qwe",
"root_password": "travis",
"run_selenium_tests": 1,
"host_name": "http://test_site_ui:8000"
}

654
yarn.lock

File diff suppressed because it is too large Load Diff