test: UI tests for org chart mobile
fix(mobile): detach node before emptying hierarchy
fix(mobile): sibling group not rendering for first level
diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js
new file mode 100644
index 0000000..6560512
--- /dev/null
+++ b/cypress/integration/test_organizational_chart_mobile.js
@@ -0,0 +1,182 @@
+context('Organizational Chart Mobile', () => {
+ before(() => {
+ cy.login();
+ cy.viewport(375, 667);
+ cy.visit('/app/website');
+
+ cy.visit(`app/organizational-chart`);
+ cy.wait(500);
+ cy.fill_field('company', 'Test Org Chart');
+ cy.get('body').click();
+ cy.wait(500);
+ });
+
+ beforeEach(() => {
+ cy.viewport(375, 667);
+ cy.wait(500);
+
+ cy.window().its('frappe').then(frappe => {
+ return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records');
+ }).as('employee_records');
+ });
+
+ it('renders root nodes', () => {
+ // check rendered root nodes and the node name, title, connections
+ cy.get('.hierarchy-mobile').find('.root-level').children()
+ .should('have.length', 2)
+ .first()
+ .as('first-child');
+
+ cy.get('@first-child').get('.node-name').contains('Test Employee 1');
+ cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO');
+ cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2');
+ });
+
+ it('expands root node', () => {
+ cy.get('@employee_records').then(employee_records => {
+ cy.get(`#${employee_records.message[1]}`)
+ .click()
+ .should('have.class', 'active');
+
+ // other root node removed
+ cy.get(`#${employee_records.message[0]}`).should('not.exist');
+
+ // children of active root node
+ cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children()
+ .should('have.length', 2)
+
+ cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node');
+ cy.get('@child-node').should('be.visible');
+
+ cy.get('@child-node')
+ .get('.node-name')
+ .contains('Test Employee 4');
+
+ // connectors between root node and immediate children
+ cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors');
+ cy.get('@connectors')
+ .should('have.length', 2)
+ .should('be.visible')
+
+ cy.get('@connectors')
+ .first()
+ .invoke('attr', 'data-child')
+ .should('eq', employee_records.message[3]);
+ });
+ });
+
+ it('expands child node', () => {
+ cy.get('@employee_records').then(employee_records => {
+ cy.get(`#${employee_records.message[3]}`)
+ .click()
+ .should('have.class', 'active')
+ .as('expanded_node');
+
+ // 2 levels on screen; 1 on active path; 1 collapsed
+ cy.get('.hierarchy-mobile').children().should('have.length', 2);
+ cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path');
+
+ // children of expanded node visible
+ cy.get('@expanded_node')
+ .next()
+ .should('have.class', 'node-children')
+ .as('node-children');
+
+ cy.get('@node-children').children().should('have.length', 1);
+ cy.get('@node-children')
+ .first()
+ .get('.node-card')
+ .should('have.class', 'active-child')
+ .contains('Test Employee 7');
+
+ // orphan connectors removed
+ cy.get(`#connectors`).children().should('have.length', 2);
+ });
+ });
+
+ it('renders sibling group', () => {
+ cy.get('@employee_records').then(employee_records => {
+ // sibling group visible for parent
+ cy.get(`#${employee_records.message[1]}`)
+ .next()
+ .as('sibling_group');
+
+ cy.get('@sibling_group')
+ .should('have.attr', 'data-parent', 'undefined')
+ .should('have.class', 'node-group')
+ .and('have.class', 'collapsed')
+
+ cy.get('@sibling_group').get('.avatar-group').children().as('siblings');
+ cy.get('@siblings').should('have.length', 1);
+ cy.get('@siblings')
+ .first()
+ .should('have.attr', 'title', 'Test Employee 1');
+
+ });
+ });
+
+ it('expands previous level nodes', () => {
+ cy.get('@employee_records').then(employee_records => {
+ cy.get(`#${employee_records.message[6]}`)
+ .click()
+ .should('have.class', 'active');
+
+ // clicking on previous level node should remove all the nodes ahead
+ // and expand that node
+ cy.get(`#${employee_records.message[3]}`).click();
+ cy.get(`#${employee_records.message[3]}`)
+ .should('have.class', 'active')
+ .should('not.have.class', 'active-path');
+
+ cy.get(`#${employee_records.message[6]}`).should('have.class', 'active-child');
+ cy.get('.hierarchy-mobile').children().should('have.length', 2);
+ cy.get(`#connectors`).children().should('have.length', 2);
+ });
+ });
+
+ it('expands sibling group', () => {
+ cy.get('@employee_records').then(employee_records => {
+ // sibling group visible for parent
+ cy.get(`#${employee_records.message[6]}`).click()
+
+ cy.get(`#${employee_records.message[3]}`)
+ .next()
+ .click();
+
+ // siblings of parent should be visible
+ cy.get('.hierarchy-mobile').prev().as('sibling_group');
+ cy.get('@sibling_group')
+ .should('exist')
+ .should('have.class', 'sibling-group')
+ .should('not.have.class', 'collapsed');
+
+ cy.get(`#${employee_records.message[1]}`)
+ .should('be.visible')
+ .should('have.class', 'active');
+
+ cy.get(`[data-parent="${employee_records.message[1]}"]`)
+ .should('be.visible')
+ .should('have.length', 2)
+ .should('have.class', 'active-child');
+ });
+ });
+
+ it('goes to the respective level after clicking on non-collapsed sibling group', () => {
+ // click on non-collapsed sibling group
+ cy.get('.hierarchy-mobile')
+ .prev()
+ .click();
+
+ // should take you to that level
+ cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2);
+ });
+
+ it('edit node navigates to employee master', () => {
+ cy.get('@employee_records').then(employee_records => {
+ cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node')
+ .click();
+
+ cy.url().should('include', `/employee/${employee_records.message[0]}`);
+ });
+ });
+});
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
index 5a6f168..bd7946a 100644
--- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
@@ -128,6 +128,9 @@
if (this.$hierarchy)
this.$hierarchy.remove();
+ if (this.$sibling_group)
+ this.$sibling_group.empty();
+
this.$hierarchy = $(
`<ul class="hierarchy-mobile">
<li class="root-level level"></li>
@@ -173,7 +176,7 @@
if (this.$sibling_group) {
const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent');
- if (node.parent_id !== sibling_parent)
+ if (node.parent_id !== undefined && node.parent_id != sibling_parent)
this.$sibling_group.empty();
}
@@ -376,9 +379,10 @@
let node_element = $(`#${node.id}`);
node_element.click(function() {
- let el = $(this).detach();
+ let el = undefined;
if (node.is_root) {
+ el = $(this).detach();
me.$hierarchy.empty();
$(`#connectors`).empty();
me.add_node_to_hierarchy(el, node);
@@ -386,6 +390,7 @@
me.remove_levels_after_node(node);
me.remove_orphaned_connectors();
} else {
+ el = $(this).detach();
me.add_node_to_hierarchy(el, node);
me.collapse_node();
}
@@ -514,10 +519,10 @@
level = $('.hierarchy-mobile > li:eq('+ level + ')');
level.nextAll('li').remove();
- let current_node = level.find(`#${node.id}`);
let node_object = this.nodes[node.id];
-
+ let current_node = level.find(`#${node.id}`).detach();
current_node.removeClass('active-child active-path');
+
node_object.expanded = 0;
node_object.$children = undefined;
diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py
index 8e67b1c..99748dc 100644
--- a/erpnext/tests/ui_test_helpers.py
+++ b/erpnext/tests/ui_test_helpers.py
@@ -11,10 +11,11 @@
emp2 = create_employee('Test Employee 2', 'CTO')
emp3 = create_employee('Test Employee 3', 'Head of Marketing and Sales', emp1)
emp4 = create_employee('Test Employee 4', 'Project Manager', emp2)
- emp5 = create_employee('Test Employee 5', 'Analyst', emp3)
- emp6 = create_employee('Test Employee 6', 'Software Developer', emp4)
+ emp5 = create_employee('Test Employee 5', 'Engineer', emp2)
+ emp6 = create_employee('Test Employee 6', 'Analyst', emp3)
+ emp7 = create_employee('Test Employee 7', 'Software Developer', emp4)
- employees = [emp1, emp2, emp3, emp4, emp5, emp6]
+ employees = [emp1, emp2, emp3, emp4, emp5, emp6, emp7]
return employees
def create_company():