diff --git a/e2e/tests/keycloak-odoo-flows.spec.ts b/e2e/tests/keycloak-odoo-flows.spec.ts index 05735f3..9c602c2 100644 --- a/e2e/tests/keycloak-odoo-flows.spec.ts +++ b/e2e/tests/keycloak-odoo-flows.spec.ts @@ -80,6 +80,7 @@ test('Coded Odoo groups create corresponding Keycloak roles.', async ({ page }) test('Creating an Odoo group creates the corresponding Keycloak role', async ({ page }) => { // setup + test.setTimeout(360000); await page.goto(`${ODOO_URL}`); await odoo.enterLoginCredentials(); await expect(page.locator('li.o_user_menu a span')).toHaveText(/administrator/i); @@ -100,6 +101,7 @@ test('Creating an Odoo group creates the corresponding Keycloak role', async ({ test('Updating a synced Odoo group updates the corresponding Keycloak role.', async ({ page }) => { // setup + test.setTimeout(720000); await page.goto(`${ODOO_URL}`); await odoo.enterLoginCredentials(); await expect(page.locator('li.o_user_menu a span')).toHaveText(/administrator/i); @@ -131,6 +133,7 @@ test('Updating a synced Odoo group updates the corresponding Keycloak role.', as test('Deleting a synced Odoo group deletes the corresponding Keycloak role.', async ({ page }) => { // setup + test.setTimeout(720000); await page.goto(`${ODOO_URL}`); await odoo.enterLoginCredentials(); await expect(page.locator('li.o_user_menu a span')).toHaveText(/administrator/i); diff --git a/e2e/tests/keycloak-openmrs-flows.spec.ts b/e2e/tests/keycloak-openmrs-flows.spec.ts index 2a36d83..7fd05d8 100644 --- a/e2e/tests/keycloak-openmrs-flows.spec.ts +++ b/e2e/tests/keycloak-openmrs-flows.spec.ts @@ -38,6 +38,7 @@ test('Logging out from OpenMRS ends the session in Keycloak and logs out the use test('Creating an OpenMRS role creates the corresponding Keycloak role.', async ({ page }) => { // setup + test.setTimeout(240000); await page.goto(`${O3_URL}/openmrs/admin/users/role.list`); // replay @@ -62,6 +63,7 @@ test('Creating an OpenMRS role creates the corresponding Keycloak role.', async test('Updating a synced OpenMRS role updates the corresponding Keycloak role.', async ({ page }) => { // setup + test.setTimeout(420000); await page.goto(`${O3_URL}/openmrs/admin/users/role.list`); await openmrs.addRole(); await keycloak.open(); @@ -98,6 +100,7 @@ test('Updating a synced OpenMRS role updates the corresponding Keycloak role.', test('Deleting a synced OpenMRS role deletes the corresponding Keycloak role.', async ({ page }) => { // setup + test.setTimeout(420000); await page.goto(`${O3_URL}/openmrs/admin/users/role.list`); await openmrs.addRole(); await keycloak.open(); diff --git a/e2e/tests/keycloak-senaite-flows.spec.ts b/e2e/tests/keycloak-senaite-flows.spec.ts index 194f9c3..4545d10 100644 --- a/e2e/tests/keycloak-senaite-flows.spec.ts +++ b/e2e/tests/keycloak-senaite-flows.spec.ts @@ -28,7 +28,6 @@ test('Logging out from SENAITE ends the session in Keycloak and logs out the use // replay await senaite.logout(); - await keycloak.confirmLogout(); await expect(page.locator('#username')).toBeVisible(); // verify diff --git a/e2e/tests/odoo-superset-flows.spec.ts b/e2e/tests/odoo-superset-flows.spec.ts index 6d23cc4..62ac815 100644 --- a/e2e/tests/odoo-superset-flows.spec.ts +++ b/e2e/tests/odoo-superset-flows.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { O3_URL, SUPERSET_URL } from '../utils/configs/globalSetup'; +import { O3_URL, ODOO_URL, SUPERSET_URL } from '../utils/configs/globalSetup'; import { Odoo } from '../utils/functions/odoo'; import { Superset } from '../utils/functions/superset'; import { OpenMRS, patientName } from '../utils/functions/openmrs'; @@ -62,6 +62,128 @@ test(`Creating an Odoo sale order line generates an entry in Superset's sale_ord await expect(unitPrice).toBe(2); }); +test(`Revising an Odoo sale order line updates the corresponding entry in Superset's sale_order_lines table.`, async ({ page }) => { + // setup + await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Complete blood count'); + await openmrs.saveLabOrder(); + await odoo.open(); + await odoo.navigateToSales(); + await odoo.createSaleOrderLine(); + const salesOrderId = await page.locator('.oe_title h1:nth-child(1) span').textContent(); + await expect(page.locator('table tbody td.o_data_cell:nth-child(2) span:nth-child(1) span')).toHaveText('Acétaminophene Co 500mg'); + await superset.open(); + await superset.selectDBSchema(); + await superset.clearSQLEditor(); + let saleOrderLinesQuery = `SELECT sale_order_name, customer_name, product_name, quantity, unit_price FROM sale_order_lines WHERE sale_order_name like '${salesOrderId}';`; + await page.getByRole('textbox').first().fill(saleOrderLinesQuery); + await superset.runSQLQuery(); + await expect(page.locator('div.virtual-table-cell:nth-child(1)')).toHaveText(`${salesOrderId}`); + await expect(page.locator('div.virtual-table-cell:nth-child(2)')).toHaveText(`${patientName.firstName + ' ' + patientName.givenName}`); + await expect(page.locator('div.virtual-table-cell:nth-child(3)')).toHaveText('Acétaminophene Co 500mg'); + await expect(page.locator('div.virtual-table-cell:nth-child(4)')).toHaveText('8'); + await expect(page.locator('div.virtual-table-cell:nth-child(5)')).toHaveText('2'); + + // replay + await page.goto(`${ODOO_URL}`); + await odoo.navigateToSales(); + await odoo.searchCustomer(); + await page.getByRole('cell', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).nth(0).click(); + await odoo.modifySaleOrderLine(); + + // verify + await page.goto(`${SUPERSET_URL}/sqllab`); + await superset.clearSQLEditor(); + await page.getByRole('textbox').first().fill(saleOrderLinesQuery); + await superset.runSQLQuery(); + await expect(page.locator('div.virtual-table-cell:nth-child(1)')).toHaveText(`${salesOrderId}`); + await expect(page.locator('div.virtual-table-cell:nth-child(2)')).toHaveText(`${patientName.firstName + ' ' + patientName.givenName}`); + await expect(page.locator('div.virtual-table-cell:nth-child(3)')).toHaveText('Acétaminophene Co 500mg'); + await expect(page.locator('div.virtual-table-cell:nth-child(4)')).toHaveText('10'); + await expect(page.locator('div.virtual-table-cell:nth-child(5)')).toHaveText('3'); +}); + +test(`Voiding an Odoo sale order line updates the corresponding entry in Superset's sale_order_lines table.`, async ({ page }) => { + // setup + await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Complete blood count'); + await openmrs.saveLabOrder(); + await odoo.open(); + await odoo.navigateToSales(); + await odoo.createSaleOrderLine(); + const salesOrderId = await page.locator('.oe_title h1:nth-child(1) span').textContent(); + await expect(page.locator('table tbody td.o_data_cell:nth-child(2) span:nth-child(1) span')).toHaveText('Acétaminophene Co 500mg'); + await superset.open(); + await superset.selectDBSchema(); + await superset.clearSQLEditor(); + let saleOrderLinesQuery = `SELECT sale_order_name, customer_name, product_name, quantity, unit_price FROM sale_order_lines WHERE sale_order_name like '${salesOrderId}';`; + await page.getByRole('textbox').first().fill(saleOrderLinesQuery); + await superset.runSQLQuery(); + await expect(page.locator('div.virtual-table-cell:nth-child(1)')).toHaveText(`${salesOrderId}`); + await expect(page.locator('div.virtual-table-cell:nth-child(2)')).toHaveText(`${patientName.firstName + ' ' + patientName.givenName}`); + await expect(page.locator('div.virtual-table-cell:nth-child(3)')).toHaveText('Acétaminophene Co 500mg'); + await expect(page.locator('div.virtual-table-cell:nth-child(4)')).toHaveText('8'); + await expect(page.locator('div.virtual-table-cell:nth-child(5)')).toHaveText('2'); + + // replay + await page.goto(`${ODOO_URL}`); + await odoo.navigateToSales(); + await odoo.searchCustomer(); + await page.getByRole('cell', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).nth(0).click(); + await odoo.voidSaleOrderLine(); + + // verify + await page.goto(`${SUPERSET_URL}/sqllab`); + await superset.clearSQLEditor(); + await page.getByRole('textbox').first().fill(saleOrderLinesQuery); + await superset.runSQLQuery(); + await expect(page.locator('div.virtual-table-cell:nth-child(1)')).toHaveText(`${salesOrderId}`); + await expect(page.locator('div.virtual-table-cell:nth-child(2)')).toHaveText(`${patientName.firstName + ' ' + patientName.givenName}`); + await expect(page.locator('div.virtual-table-cell:nth-child(3)')).toHaveText('Acétaminophene Co 500mg'); + await expect(page.locator('div.virtual-table-cell:nth-child(4)')).not.toHaveText('8'); + await expect(page.locator('div.virtual-table-cell:nth-child(4)')).toHaveText('0'); +}); + +test(`Deleting an Odoo quotation line deletes the corresponding entry in Superset's sale_order_lines table.`, async ({ page }) => { + // setup + await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Complete blood count'); + await openmrs.saveLabOrder(); + await odoo.open(); + await odoo.navigateToSales(); + await odoo.createQuotationLine(); + const salesOrderId = await page.locator('.oe_title h1:nth-child(1) span').textContent(); + await expect(page.locator('table tbody td.o_data_cell:nth-child(2) span:nth-child(1) span')).toHaveText(/acyclovir Sirop 200mg/i); + await superset.open(); + await superset.selectDBSchema(); + await superset.clearSQLEditor(); + let saleOrderLinesQuery = `SELECT sale_order_name, customer_name, product_name, quantity, unit_price FROM sale_order_lines WHERE sale_order_name like '${salesOrderId}';`; + await page.getByRole('textbox').first().fill(saleOrderLinesQuery); + await superset.runSQLQuery(); + await expect(page.locator('div.virtual-table-cell:nth-child(1)')).toHaveText(`${salesOrderId}`); + await expect(page.locator('div.virtual-table-cell:nth-child(2)')).toHaveText(`${patientName.firstName + ' ' + patientName.givenName}`); + await expect(page.locator('div.virtual-table-cell:nth-child(3)')).toHaveText(/acyclovir Sirop 200mg/i); + await expect(page.locator('div.virtual-table-cell:nth-child(4)')).toHaveText('6'); + await expect(page.locator('div.virtual-table-cell:nth-child(5)')).toHaveText('2'); + + // replay + await page.goto(`${ODOO_URL}`); + await odoo.navigateToSales(); + await odoo.searchCustomer(); + await page.getByRole('cell', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).nth(0).click(); + await odoo.deleteQuotationLine(); + + // verify + await page.goto(`${SUPERSET_URL}/sqllab`); + await superset.clearSQLEditor(); + await page.getByRole('textbox').first().fill(saleOrderLinesQuery); + await superset.runSQLQuery(); + await expect(page.locator('div.ant-alert-message')).toHaveText(/the query returned no data/i); +}); + test(`A (synced) sale order line in Odoo generates an entry in Superset's sale_order_lines table.`, async ({ page }) => { // setup await superset.open(); diff --git a/e2e/tests/openmrs-superset-flows.spec.ts b/e2e/tests/openmrs-superset-flows.spec.ts index 4fb5d74..9b1a110 100644 --- a/e2e/tests/openmrs-superset-flows.spec.ts +++ b/e2e/tests/openmrs-superset-flows.spec.ts @@ -84,7 +84,7 @@ test(`Creating an OpenMRS visit creates the visit in Superset's visits table.`, await expect(page.locator('div.virtual-table-cell:nth-child(3)')).toHaveText('Inpatient Ward'); await expect(page.locator('div.virtual-table-cell:nth-child(6)')).toHaveText('Facility Visit'); await expect(page.locator('div.virtual-table-cell:nth-child(8)')).toHaveText('M'); - await expect(page.locator('div.virtual-table-cell:nth-child(10)')).toHaveText('true') + await expect(page.locator('div.virtual-table-cell:nth-child(2)')).toHaveText('false') await page.getByRole('tab', { name: 'Query history' }).click(); await superset.clearSQLEditor(); await openmrs.voidPatient(); diff --git a/e2e/utils/functions/odoo.ts b/e2e/utils/functions/odoo.ts index 7a24381..0da7d77 100644 --- a/e2e/utils/functions/odoo.ts +++ b/e2e/utils/functions/odoo.ts @@ -41,7 +41,7 @@ export class Odoo { } async createSaleOrderLine() { - await this.page.getByRole('button', { name: 'Create' }).click(); + await this.page.getByRole('button', { name: /create/i }).click(); await expect(this.page.locator('li.breadcrumb-item:nth-child(2)')).toHaveText(/new/i); await this.page.getByLabel('Customer', { exact: true }).type(`${patientName.firstName + ' ' + patientName.givenName}`); await this.page.getByText(`${patientName.firstName + ' ' + patientName.givenName}`).first().click(); @@ -52,13 +52,53 @@ export class Odoo { await this.page.locator('td.o_data_cell:nth-child(7) input').fill('2.00'); await this.page.locator('td.o_data_cell:nth-child(9)').click(), delay(2000); await expect(this.page.locator('td.o_data_cell:nth-child(9)')).toHaveText('$ 16.00'); - await this.page.getByRole('button', { name: 'Confirm' }).click(), delay(3000); + await this.page.getByRole('button', { name: /confirm/i }).click(), delay(3000); await expect(this.page.locator('td.o_data_cell:nth-child(2) span:nth-child(1) span')).toHaveText('Acétaminophene Co 500mg'); await expect(this.page.locator('td.o_data_cell:nth-child(4)')).toHaveText('8'); await expect(this.page.locator('td.o_data_cell:nth-child(9)')).toHaveText('2.00'); await expect(this.page.locator('td.o_data_cell:nth-child(11)')).toHaveText('$ 16.00'); } + async createQuotationLine() { + await this.page.getByRole('button', { name: /create/i }).click(); + await expect(this.page.locator('li.breadcrumb-item:nth-child(2)')).toHaveText(/new/i); + await this.page.getByLabel('Customer', { exact: true }).type(`${patientName.firstName + ' ' + patientName.givenName}`); + await this.page.getByText(`${patientName.firstName + ' ' + patientName.givenName}`).first().click(); + await this.page.getByRole('button', { name: 'Add a product' }).click(); + await this.page.locator('td.o_data_cell:nth-child(2) div:nth-child(1) input').fill('Acyclovir Sirop 200mg'); + await this.page.getByText('Acyclovir Sirop 200mg').first().click(); + await this.page.locator('input[name="product_uom_qty"]').fill('6'); + await this.page.locator('td.o_data_cell:nth-child(7) input').fill('2.00'); + await this.page.locator('td.o_data_cell:nth-child(9)').click(), delay(2000); + await expect(this.page.locator('td.o_data_cell:nth-child(9)')).toHaveText('$ 12.00'); + await this.page.getByRole('button', { name: /save/i }).click(), delay(3000); + await expect(this.page.locator('td.o_data_cell:nth-child(2) span:nth-child(1) span')).toHaveText('Acyclovir Sirop 200mg'); + } + + async modifySaleOrderLine() { + await this.page.getByRole('button', { name: /edit/i }).click(); + await this.page.getByText(/acétaminophene co 500mg/i).nth(1).click(); + await this.page.locator('input[name="product_uom_qty"]').fill('10'); + await this.page.locator('input[name="price_unit"]').fill('3'); + await this.page.locator('td.o_field_x2many_list_row_add').click(), delay(2000); + await expect(this.page.locator('td.o_data_cell:nth-child(11)')).toHaveText('$ 30.00'); + await this.page.getByRole('button', { name: /save/i }).click(), delay(3000); + } + + async voidSaleOrderLine() { + await this.page.getByRole('button', { name: /edit/i }).click(); + await this.page.getByText(/acétaminophene co 500mg/i).nth(1).click(); + await this.page.locator('input[name="product_uom_qty"]').fill('0'); + await this.page.getByRole('button', { name: /save/i }).click(), delay(1000); + await this.page.getByRole('button', { name: 'Ok' }).click(), delay(3000); + } + + async deleteQuotationLine() { + await this.page.getByRole('button', { name: /edit/i }).click(); + await this.page.getByRole('cell', { name: /delete row/i }).click(); + await this.page.getByRole('button', { name: /save/i }).click(), delay(3000); + } + async activateDeveloperMode() { await this.navigateToSettings(); await expect(this.page.locator('#devel_tool a:nth-child(1)')).toBeVisible(); diff --git a/e2e/utils/functions/openmrs.ts b/e2e/utils/functions/openmrs.ts index d71b155..ea00398 100644 --- a/e2e/utils/functions/openmrs.ts +++ b/e2e/utils/functions/openmrs.ts @@ -50,7 +50,7 @@ export class OpenMRS { await this.page.locator('#username').fill(`${process.env.OZONE_USERNAME}`); await this.page.getByRole('button', { name: /continue/i }).click(); await this.page.locator('#password').fill(`${process.env.OZONE_PASSWORD}`); - await this.page.getByRole('button', { name: /sign in/ }).click(); + await this.page.getByRole('button', { name: /sign in/i }).click(); } async createPatient() { @@ -68,10 +68,8 @@ export class OpenMRS { await this.page.locator('div[aria-label="day, "]').fill('16'); await this.page.locator('div[aria-label="month, "]').fill('08'); await this.page.locator('div[aria-label="year, "]').fill('2002'); - await this.page.getByRole('button', { name: /register patient/i }).click(); await this.createPatientButton().click(); await expect(this.page.getByText(/new patient created/i)).toBeVisible(), delay(3000);; - await this.page.getByRole('button', { name: 'Close', exact: true }).click(); } async goToHomePage() { @@ -178,7 +176,7 @@ export class OpenMRS { async addPatientAppointment() { await this.page.getByRole('link', { name: /appointments/i }).click(); - await this.page.getByRole('button', { name: /add/i, exact: true }).click(); + await this.page.getByRole('button', { name: 'Add', exact: true }).click(); await this.page.getByLabel(/select a service/i).selectOption('General Medicine service'); await this.page.getByLabel(/select an appointment type/i).selectOption('Scheduled'); await this.page.locator('#duration').fill('40'); @@ -202,8 +200,8 @@ export class OpenMRS { async navigateToLabOrderForm() { await this.page.getByLabel(/order basket/i).click(), delay(2000); - await expect(this.page.getByRole('button', { name: /add/i, exact: true }).nth(1)).toBeVisible(); - await this.page.getByRole('button', { name: /add/i, exact: true }).nth(1).click(); + await expect(this.page.getByRole('button', { name: 'Add', exact: true }).nth(1)).toBeVisible(); + await this.page.getByRole('button', { name: 'Add', exact: true }).nth(1).click(); } async saveLabOrder() { @@ -227,8 +225,7 @@ export class OpenMRS { await this.page.getByRole('button', { name: /options/i, exact: true }).click(); await this.page.getByRole('menuitem', { name: /delete this encounter/i }).click(); await this.page.getByRole('button', { name: /danger delete/i }).click(); - await expect(this.page.getByText(/encounter deleted/i)).toBeVisible(); - await expect(this.page.getByText(/encounter successfully deleted/i)).toBeVisible(), delay(5000); + await expect(this.page.getByText(/encounter deleted/i)).toBeVisible(), delay(5000); } async cancelLabOrder() { @@ -248,8 +245,8 @@ export class OpenMRS { async navigateToDrugOrderForm() { await expect(this.page.getByLabel(/order basket/i)).toBeVisible(); await this.page.getByLabel(/order basket/i).click(), delay(2000); - await expect(this.page.getByRole('button', { name: /add/i, exact: true }).nth(0)).toBeVisible(); - await this.page.getByRole('button', { name: /add/i, exact: true }).nth(0).click(); + await expect(this.page.getByRole('button', { name: 'Add', exact: true }).nth(0)).toBeVisible(); + await this.page.getByRole('button', { name: 'Add', exact: true }).nth(0).click(); } async fillDrugOrderForm() { @@ -278,7 +275,7 @@ export class OpenMRS { async createDrugOrderWithFreeTextDosage() { await expect(this.page.getByLabel(/order basket/i)).toBeVisible(); await this.page.getByLabel(/order basket/i).click(), delay(2000); - await expect(this.page.getByRole('button', { name: /add/i, exact: true }).nth(0)).toBeVisible(); + await expect(this.page.getByRole('button', { name: 'Add', exact: true }).nth(0)).toBeVisible(); await this.page.getByRole('button', { name: 'Add', exact: true }).nth(0).click(); await this.page.getByRole('searchbox').fill('Aspirin 325mg'); await this.page.getByRole('button', { name: 'Order form' }).click(); diff --git a/playwright.config.ts b/playwright.config.ts index 8b99f51..d4222e6 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,7 +5,7 @@ dotenv.config(); const config: PlaywrightTestConfig = { testDir: './e2e/tests', - timeout: 10 * 60 * 1000, + timeout: 3 * 60 * 1000, expect: { timeout: 40 * 1000, },