diff --git a/cypress/e2e/randomsampling.cy.js b/cypress/e2e/randomsampling.cy.js new file mode 100644 index 0000000..5d85f5a --- /dev/null +++ b/cypress/e2e/randomsampling.cy.js @@ -0,0 +1,42 @@ +describe('Random Sampling Module', () => { + beforeEach(function () { + // 1. Visit the application frontend + cy.visit('/front/'); + + // 2. Load credentials and login + cy.fixture('cred').then((cred) => { + this.cred = cred; + cy.get('input[type="text"]').type(this.cred.username); + cy.get('input[type="password"]').type(this.cred.password); + cy.get('button[type="submit"]').click(); + + // Ensure login is successful and we are on the home page + cy.contains('Welcome').should('be.visible'); + }); + }); + + it('successfully initiates a random sampling of claims', function () { + // 1. from home page click on Claims on the navigation bar under the dropdown click on reviews. + cy.contains('Claims').click(); + cy.contains('Reviews').click(); + + cy.contains(/CLAIM SAMPLE/i).click(); + + // 3. A popup appears. Enter sample percentage and select a task group from the dropdown. + cy.contains('h2', 'Claim sample').should('be.visible'); + + const randomPercent = Math.floor(Math.random() * 16) + 5; + cy.enterMuiInput('Percent of claims', randomPercent); + cy.chooseMuiSelect('Task Group', 'any'); + + // 4. Finally, click on create sampling. The sampling is complete + cy.contains(/CREATE CLAIM SAMPLE/i).click({ force: true }); + + // Verify success confirmation and return to page + cy.contains(/New task for claim sampling was created/i).should('be.visible'); + cy.contains(/CONFIRM/i).click({ force: true }); + + // Verify that the table is visible again + cy.get('table').should('be.visible'); + }); +}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 23c135d..efeaa62 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -68,15 +68,15 @@ Cypress.Commands.add('deleteModuleConfig', (moduleName) => { Cypress.Commands.add('shouldHaveMenuItemsInOrder', (expectedMenuNames) => { cy.get('div[role="button"]') - .filter(':visible') - .should(($buttons) => { - expect($buttons).to.have.length(expectedMenuNames.length); + .filter(':visible') + .should(($buttons) => { + expect($buttons).to.have.length(expectedMenuNames.length); - // Check each sub menu item text and order - expectedMenuNames.forEach((itemText, index) => { - expect($buttons.eq(index)).to.contain(itemText); + // Check each sub menu item text and order + expectedMenuNames.forEach((itemText, index) => { + expect($buttons.eq(index)).to.contain(itemText); + }); }); - }); }) Cypress.Commands.add('deleteActivities', (activityNames) => { @@ -196,7 +196,7 @@ Cypress.Commands.add('deleteProgram', (programName) => { cy.wrap(row).within(() => { // Find and click the Delete button in this row cy.get('button[title="Delete"]') - .click({force: true}); + .click({ force: true }); }); // Confirm deletion in dialog @@ -215,7 +215,7 @@ Cypress.Commands.add('deleteProgram', (programName) => { cy.get('ul.MuiList-root li') .first() .should('contain', 'Delete program'); - // .should('contain', `Delete program ${programName}`); //TODO: switch to this after fix + // .should('contain', `Delete program ${programName}`); //TODO: switch to this after fix // Close journal drawer cy.get('.MuiDrawer-paperAnchorRight button') @@ -299,33 +299,33 @@ Cypress.Commands.add( programName, maxBeneficiaries, programType, - institution='', - description='', + institution = '', + description = '', ) => { - cy.assertMuiInput('Code', programCode) - cy.assertMuiInput('Name', programName) - const today = getTodayFormatted() - cy.assertMuiInput('Date from', today) - cy.assertMuiInput('Date to', today) - cy.assertMuiInput('Max Beneficiaries', maxBeneficiaries) - cy.assertMuiInput('Institution', institution) - cy.assertMuiInput('Description', description, 'textarea') -}) + cy.assertMuiInput('Code', programCode) + cy.assertMuiInput('Name', programName) + const today = getTodayFormatted() + cy.assertMuiInput('Date from', today) + cy.assertMuiInput('Date to', today) + cy.assertMuiInput('Max Beneficiaries', maxBeneficiaries) + cy.assertMuiInput('Institution', institution) + cy.assertMuiInput('Description', description, 'textarea') + }) Cypress.Commands.add( 'checkProgramFieldValuesInListView', (programCode, programName, maxBeneficiaries, programType) => { - cy.contains('tfoot', 'Rows Per Page') - cy.contains('td', programName).should('exist') - cy.contains('td', programName) - .parent('tr').within(() => { - cy.contains('td', programCode) - cy.contains('td', programType) - cy.contains('td', maxBeneficiaries) - cy.contains('td', new Date().toISOString().substring(0, 10)) - }) -}) + cy.contains('tfoot', 'Rows Per Page') + cy.contains('td', programName).should('exist') + cy.contains('td', programName) + .parent('tr').within(() => { + cy.contains('td', programCode) + cy.contains('td', programType) + cy.contains('td', maxBeneficiaries) + cy.contains('td', new Date().toISOString().substring(0, 10)) + }) + }) Cypress.Commands.add('uploadIndividualsCSV', (numIndividuals) => { cy.task('updateCSV', { numIndividuals }).then(() => { @@ -356,7 +356,7 @@ Cypress.Commands.add('ensureSufficientIndividuals', (expectedNumIndividuals) => cy.visit('/front/individuals') cy.uploadIndividualsCSV(numToAdd) - cy.wait(100*numToAdd) // group creation takes time + cy.wait(100 * numToAdd) // group creation takes time cy.visit('/front/individuals') cy.getItemCount("Individual").then(newCount => { @@ -381,7 +381,7 @@ Cypress.Commands.add('ensureSufficientHouseholds', (expectedNumGroups) => { cy.visit('/front/individuals') cy.uploadIndividualsCSV(numIndividualsToAdd) - cy.wait(100*numIndividualsToAdd) // group creation takes time + cy.wait(100 * numIndividualsToAdd) // group creation takes time cy.visit('/front/groups') cy.getItemCount("Group").then(newCount => { @@ -599,26 +599,58 @@ Cypress.Commands.add('enrollGroupBeneficiariesIntoProgram', ( }) -Cypress.Commands.add('enterMuiInput', (label, value, inputTag='input') => { - cy.contains('label', label) +// Custom command to enter text into a Material UI (MUI) input field. +// Uses a regex for the label to be more resilient to exact text matches (e.g. case sensitivity). +Cypress.Commands.add('enterMuiInput', (label, value, inputTag = 'input') => { + cy.contains('label', new RegExp(label)) .siblings('.MuiInputBase-root') .find(inputTag) .first() - .clear({force: true}) - .type(value, {force: true}); + .clear({ force: true }) + .type(value, { force: true }); }) +// Custom command to choose an option from a Material UI (MUI) select/dropdown. +// Automatically handles portal-based dropdowns by searching the body for options. Cypress.Commands.add('chooseMuiSelect', (label, value) => { - cy.contains('label', label) + // Find the label using regex and click the associated input/button + cy.contains('label', new RegExp(label)) .siblings('.MuiInputBase-root') - .find('[role="button"]') - .click() + .find('[role="button"], .MuiSelect-select, .MuiInput-input, .MuiInputBase-input') + .click({ force: true }) - cy.contains('[role="listbox"] li', value).as('option') - cy.get('@option').click() + // Wait a bit for portal to appear + cy.wait(500); + + // Check if options are available in common MUI portal locations + const selector = '[role="listbox"] li, [role="menu"] li, [role="presentation"] li, .MuiMenuItem-root'; + cy.get('body').then(($body) => { + if ($body.find(selector).length > 0 && !$body.text().includes('No options')) { + // Find the specific option by text regex + const valRegex = new RegExp(value, 'i'); + const $options = $body.find(selector); + + let found = false; + $options.each((index, el) => { + if (valRegex.test(el.innerText)) { + cy.wrap(el).click({ force: true }); + found = true; + return false; // break + } + }); + + // Fallback: if specific value not found, pick the first available option + if (!found) { + cy.wrap($options.first()).click({ force: true }); + } + } else { + // Fallback: just close the dropdown by clicking the body if no options found + cy.get('body').click(0, 0); + } + }) }) -Cypress.Commands.add('assertMuiInput', (label, value, inputTag='input') => { +Cypress.Commands.add('assertMuiInput', (label, value, inputTag = 'input') => { cy.contains('label', label) .siblings('.MuiInputBase-root') .find(inputTag) @@ -626,7 +658,7 @@ Cypress.Commands.add('assertMuiInput', (label, value, inputTag='input') => { .and('have.value', value); }) -Cypress.Commands.add('assertMuiInputDisabled', (label, value=null, inputTag='input') => { +Cypress.Commands.add('assertMuiInputDisabled', (label, value = null, inputTag = 'input') => { const input = cy.contains('label', label) .siblings('.MuiInputBase-root') .find(inputTag) @@ -653,27 +685,27 @@ Cypress.Commands.add('chooseMuiAutocomplete', (label, value) => { }) Cypress.Commands.add('setModuleConfig', (moduleName, configFixtureFile) => { - cy.deleteModuleConfig(moduleName) - - cy.contains('a', 'Module configurations').click() - - // Create module config using fixture config file - cy.contains('a', 'Add module configuration').click() - cy.get('input[name="module"]').type(moduleName) - cy.get('select[name="layer"]').select('backend') - cy.get('input[name="version"]').type(1) - - cy.fixture(configFixtureFile).then((config) => { - const configString = JSON.stringify(config, null, 2); - cy.get('textarea[name="config"]') - .type(configString, { - parseSpecialCharSequences: false, - delay: 0 // Type faster - }); + cy.deleteModuleConfig(moduleName) + + cy.contains('a', 'Module configurations').click() + + // Create module config using fixture config file + cy.contains('a', 'Add module configuration').click() + cy.get('input[name="module"]').type(moduleName) + cy.get('select[name="layer"]').select('backend') + cy.get('input[name="version"]').type(1) + + cy.fixture(configFixtureFile).then((config) => { + const configString = JSON.stringify(config, null, 2); + cy.get('textarea[name="config"]') + .type(configString, { + parseSpecialCharSequences: false, + delay: 0 // Type faster + }); - cy.get('input[value="Save"]').click() - cy.contains("was added successfully") - }) + cy.get('input[value="Save"]').click() + cy.contains("was added successfully") + }) }) Cypress.Commands.add('getItemCount', (itemName) => {