diff --git a/e2e/helpers/extensions.js b/e2e/helpers/extensions.js index 2484728f4..36fbd2c38 100644 --- a/e2e/helpers/extensions.js +++ b/e2e/helpers/extensions.js @@ -65,15 +65,6 @@ let actions = { }, doLogin: async function(userName, password = 'nightmare') { - await this.waitForSelector(`vn-login vn-textfield[ng-model="$ctrl.user"]`, {visible: true}); - await this.clearInput(`vn-login vn-textfield[ng-model="$ctrl.user"]`); - await this.write(`vn-login vn-textfield[ng-model="$ctrl.user"]`, userName); - await this.clearInput(`vn-login vn-textfield[ng-model="$ctrl.password"]`); - await this.write(`vn-login vn-textfield[ng-model="$ctrl.password"]`, password); - await this.waitToClick('vn-login button[type=submit]'); - }, - - login: async function(userName) { let state = await this.getState(); if (state != 'login') { @@ -91,6 +82,16 @@ let actions = { } await this.waitForState('login'); + + await this.waitForSelector(`vn-login vn-textfield[ng-model="$ctrl.user"]`, {visible: true}); + await this.clearInput(`vn-login vn-textfield[ng-model="$ctrl.user"]`); + await this.write(`vn-login vn-textfield[ng-model="$ctrl.user"]`, userName); + await this.clearInput(`vn-login vn-textfield[ng-model="$ctrl.password"]`); + await this.write(`vn-login vn-textfield[ng-model="$ctrl.password"]`, password); + await this.waitToClick('vn-login button[type=submit]'); + }, + + login: async function(userName) { await this.doLogin(userName); await this.waitForState('home'); }, @@ -114,10 +115,11 @@ let actions = { }, gotoState: async function(state, params) { - return await this.evaluate((state, params) => { + await this.evaluate((state, params) => { let $state = angular.element(document.body).injector().get('$state'); return $state.go(state, params); }, state, params); + await this.waitForSpinnerLoad(); }, waitForState: async function(state) { @@ -125,7 +127,7 @@ let actions = { let $state = angular.element(document.body).injector().get('$state'); return !$state.transition && $state.is(state); }, {}, state); - await this.waitForSpinnerLoad(state); + await this.waitForSpinnerLoad(); }, waitForTransition: async function() { @@ -181,6 +183,12 @@ let actions = { }, selector, property); }, + getClassName: async function(selector) { + const element = await this.$(selector); + const handle = await element.getProperty('className'); + return await handle.jsonValue(); + }, + waitPropertyLength: async function(selector, property, minLength) { await this.waitForFunction((selector, property, minLength) => { const element = document.querySelector(selector); @@ -339,24 +347,42 @@ let actions = { await this.waitFor(300); await this.evaluate(() => { - let hideButton = document.querySelector('#shapes .shown button'); + let hideButton = document + .querySelector('vn-snackbar .shape.shown button'); if (hideButton) - return document.querySelector('#shapes .shown button').click(); + return hideButton.click(); }); - await this.waitFor('#shapes > .shape', {hidden: true}); + await this.waitFor('vn-snackbar .shape.shown', {hidden: true}); + }, + + waitForSnackbar: async function() { + const selector = 'vn-snackbar .shape.shown'; + await this.waitForSelector(selector); + + let message = await this.evaluate(selector => { + const shape = document.querySelector(selector); + const message = { + text: shape.querySelector('.text').innerText + }; + + const types = ['error', 'success']; + for (let type of types) { + if (shape.classList.contains(type)) { + message.type = type; + break; + } + } + + return message; + }, selector); + + await this.hideSnackbar(); + return message; }, waitForLastSnackbar: async function() { - const selector = 'vn-snackbar .shown .text'; - - await this.waitForSelector(selector); - let snackBarText = await this.evaluate(selector => { - const shape = document.querySelector(selector); - - return shape.innerText; - }, selector); - await this.hideSnackbar(); - return snackBarText; + const message = await this.waitForSnackbar(); + return message.text; }, pickDate: async function(selector, date) { @@ -438,7 +464,6 @@ let actions = { .includes(searchValue.toLowerCase()); }, {}, builtSelector, searchValue); - await this.waitForMutation('.vn-drop-down', 'childList'); await this.waitFor('.vn-drop-down', {hidden: true}); }, diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 5469c09df..f9b2a0113 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -2,6 +2,8 @@ export default { globalItems: { applicationsMenuButton: '#apps', + userMenuButton: '#user', + logoutButton: '#logout', applicationsMenuVisible: '.modules-menu', clientsButton: '.modules-menu > li[ui-sref="client.index"]', itemsButton: '.modules-menu > li[ui-sref="item.index"]', @@ -10,7 +12,6 @@ export default { claimsButton: '.modules-menu > li[ui-sref="claim.index"]', returnToModuleIndexButton: 'a[ui-sref="order.index"]', homeButton: 'vn-topbar > div.side.start > a', - userMenuButton: '#user', userLocalWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.localWarehouseFk"]', userLocalBank: '.user-popover vn-autocomplete[ng-model="$ctrl.localBankFk"]', userLocalCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.localCompanyFk"]', @@ -338,8 +339,8 @@ export default { }, itemDiary: { secondTicketId: 'vn-item-diary vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(2) > span', + fourthBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(4) > vn-td.balance > span', firstBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(1) > vn-td.balance', - fourthBalance: 'vn-item-diary vn-tbody > vn-tr:nth-child(4) > vn-td.balance', warehouse: 'vn-item-diary vn-autocomplete[ng-model="$ctrl.warehouseFk"]', }, itemLog: { @@ -352,7 +353,7 @@ export default { route: 'vn-ticket-summary vn-label-value[label="Route"] > section > span > span', total: 'vn-ticket-summary vn-one.taxes > p:nth-child(3) > strong', sale: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr', - firstSaleItemId: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > span', + firstSaleItemId: 'vn-ticket-summary [name="sales"] vn-table vn-tbody > :nth-child(1) > vn-td:nth-child(2) > span', firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img', itemDescriptorPopover: '.vn-popover.shown vn-item-descriptor', itemDescriptorPopoverItemDiaryButton: 'vn-item-descriptor a[href="#!/item/2/diary?warehouseFk=5&ticketFk=20"]', diff --git a/e2e/paths/01-login/01_login.spec.js b/e2e/paths/01-login/01_login.spec.js index 6b101b00b..ed507df4e 100644 --- a/e2e/paths/01-login/01_login.spec.js +++ b/e2e/paths/01-login/01_login.spec.js @@ -1,3 +1,4 @@ +import selectors from '../../helpers/selectors'; import getBrowser from '../../helpers/puppeteer'; describe('Login path', async() => { @@ -13,41 +14,54 @@ describe('Login path', async() => { }); describe('Bad login', async() => { - it('should receive an error when the username is incorrect', async() => { - await page.doLogin('badUser', ''); - const result = await page.waitForLastSnackbar(); + it('should receive an error when the password is invalid', async() => { + await page.doLogin('employee', 'badPassword'); + const message = await page.waitForSnackbar(); const state = await page.getState(); - expect(result.length).toBeGreaterThan(0); + expect(message.type).toBe('error'); + expect(state).toBe('login'); + }); + + it('should receive an error when the username is invalid', async() => { + await page.doLogin('badUser', ''); + const message = await page.waitForSnackbar(); + const state = await page.getState(); + + expect(message.type).toBe('error'); expect(state).toBe('login'); }); it('should receive an error when the username is blank', async() => { await page.doLogin('', ''); - const result = await page.waitForLastSnackbar(); + const message = await page.waitForSnackbar(); const state = await page.getState(); - expect(result.length).toBeGreaterThan(0); - expect(state).toBe('login'); - }); - - it('should receive an error when the password is incorrect', async() => { - await page.doLogin('employee', 'badPassword'); - const result = await page.waitForLastSnackbar(); - const state = await page.getState(); - - expect(result.length).toBeGreaterThan(0); + expect(message.type).toBe('error'); expect(state).toBe('login'); }); }); describe('Successful login', async() => { - it('should log in', async() => { - await page.doLogin('employee', 'nightmare'); - await page.waitForNavigation(); + it('should log in and go to home state', async() => { + await page.doLogin('employee'); + await page.waitFor('vn-home'); const state = await page.getState(); expect(state).toBe('home'); }); }); + + describe('Logout', async() => { + it('should logout and return to login state', async() => { + await page.doLogin('employee'); + + await page.waitToClick(selectors.globalItems.userMenuButton); + await page.waitToClick(selectors.globalItems.logoutButton); + await page.waitFor('vn-login'); + const state = await page.getState(); + + expect(state).toBe('login'); + }); + }); }); diff --git a/e2e/paths/02-client/01_create_client.spec.js b/e2e/paths/02-client/01_create_client.spec.js index 0b8c96c16..79c50fe11 100644 --- a/e2e/paths/02-client/01_create_client.spec.js +++ b/e2e/paths/02-client/01_create_client.spec.js @@ -1,9 +1,10 @@ import selectors from '../../helpers/selectors'; import getBrowser from '../../helpers/puppeteer'; -describe('Client create path', async() => { +describe('Client create path', () => { let browser; let page; + beforeAll(async() => { browser = await getBrowser(); page = browser.page; @@ -92,9 +93,9 @@ describe('Client create path', async() => { await page.clearInput(selectors.createClientView.postcode); await page.write(selectors.createClientView.postcode, '46000'); await page.waitToClick(selectors.createClientView.createButton); - const result = await page.waitForLastSnackbar(); + const message = await page.waitForSnackbar(); - expect(result).toEqual('Data saved!'); + expect(message.type).toEqual('success'); }); it('should click on the Clients button of the top bar menu', async() => { diff --git a/e2e/paths/05-ticket/11_diary.spec.js b/e2e/paths/05-ticket/11_diary.spec.js index 5e900fd25..71c2fa881 100644 --- a/e2e/paths/05-ticket/11_diary.spec.js +++ b/e2e/paths/05-ticket/11_diary.spec.js @@ -1,66 +1,30 @@ import selectors from '../../helpers/selectors.js'; import getBrowser from '../../helpers/puppeteer'; -// #2026 Fallo en relocate de descriptor popover -xdescribe('Ticket diary path', () => { - let browser; +describe('Ticket diary path', () => { let page; beforeAll(async() => { - browser = await getBrowser(); - page = browser.page; + page = (await getBrowser()).page; await page.loginAndModule('employee', 'ticket'); }); afterAll(async() => { - await browser.close(); + await page.browser().close(); }); - it('should search for a specific ticket', async() => { - await page.write(selectors.ticketsIndex.topbarSearch, '1'); - await page.waitToClick(selectors.ticketsIndex.searchButton); - await page.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1); - const result = await page.countElement(selectors.ticketsIndex.searchResult); - - expect(result).toEqual(1); - }); - - it(`should click on the search result to access to the ticket summary`, async() => { - await page.waitForTextInElement(selectors.ticketsIndex.searchResult, 'Bat cave'); - await page.waitToClick(selectors.ticketsIndex.searchResult); - let url = await page.expectURL('/summary'); // use waitForState instead - - expect(url).toBe(true); - }); - - it(`should navigate to the item diary from the 1st sale item id descriptor popover`, async() => { + it(`should navigate to item diary from ticket sale and check the lines`, async() => { + await page.accessToSearchResult('1'); await page.waitToClick(selectors.ticketSummary.firstSaleItemId); - await page.waitForTransitionEnd('.vn-popover'); await page.waitToClick(selectors.ticketSummary.popoverDiaryButton); - let url = await page.expectURL('/diary'); // use waitForState instead + await page.waitForState('item.card.diary'); - expect(url).toBe(true); - }); + const secondIdClass = await page.getClassName(selectors.itemDiary.secondTicketId); + const fourthBalanceClass = await page.getClassName(selectors.itemDiary.fourthBalance); + const firstBalanceClass = await page.getClassName(selectors.itemDiary.firstBalance); - it(`should check the second line id is marked as message`, async() => { - const result = await page - .waitToGetProperty(selectors.itemDiary.secondTicketId, 'className'); - - expect(result).toContain('message'); - }); - - it(`should check the third line balance is marked as message`, async() => { - const result = await page - .waitToGetProperty(`${selectors.itemDiary.fourthBalance} > span`, 'className'); - - expect(result).toContain('message'); - }); - - it(`should change to the warehouse two and check there are sales marked as negative balance`, async() => { - await page.autocompleteSearch(selectors.itemDiary.warehouse, 'Warehouse Two'); - const result = await page - .waitToGetProperty(selectors.itemDiary.firstBalance, 'className'); - - expect(result).toContain('balance'); + expect(secondIdClass).toContain('message'); + expect(fourthBalanceClass).toContain('message'); + expect(firstBalanceClass).toContain('balance'); }); }); diff --git a/front/core/services/auth.js b/front/core/services/auth.js index f59c4c0dd..a1dcfa395 100644 --- a/front/core/services/auth.js +++ b/front/core/services/auth.js @@ -45,8 +45,11 @@ export default class Auth { } login(user, password, remember) { - if (!user) - return this.$q.reject(new UserError('Please enter your username')); + if (!user) { + let err = new UserError('Please enter your username'); + err.code = 'EmptyLogin'; + return this.$q.reject(err); + } let params = { user, diff --git a/gulpfile.js b/gulpfile.js index eb421ba9b..3d9adfded 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -181,6 +181,13 @@ function e2eSingleRun() { return gulp.src(specFiles).pipe(jasmine({ errorOnFail: false, timeout: 30000, + config: { + random: false, + // TODO: Waiting for this option to be implemented + // https://github.com/jasmine/jasmine/issues/1533 + stopSpecOnExpectationFailure: false + + }, reporter: [ new SpecReporter({ spec: { diff --git a/modules/item/front/descriptor/style.scss b/modules/item/front/descriptor/style.scss index c4847bead..336bfdf53 100644 --- a/modules/item/front/descriptor/style.scss +++ b/modules/item/front/descriptor/style.scss @@ -3,6 +3,7 @@ vn-item-descriptor { display: block; img[ng-src] { + min-height: 16em; height: 100%; width: 100%; display: block;