From f054eca099861588434df2f67293e09e233cc97b Mon Sep 17 00:00:00 2001 From: Lavisha Y Date: Fri, 10 Apr 2026 17:08:15 -0700 Subject: [PATCH 1/2] added drawer page testscripts --- .../config/wdio.config.js | 144 +++++++++--------- .../page_objects/ballotDrawer.browser.js | 88 +++++++++++ .../specs/BalletDrawerPage.browser.js | 66 ++++++++ 3 files changed, 227 insertions(+), 71 deletions(-) create mode 100644 tests/browserstack_automation/page_objects/ballotDrawer.browser.js create mode 100644 tests/browserstack_automation/specs/BalletDrawerPage.browser.js diff --git a/tests/browserstack_automation/config/wdio.config.js b/tests/browserstack_automation/config/wdio.config.js index 4e00d1e3f..4418a91a7 100644 --- a/tests/browserstack_automation/config/wdio.config.js +++ b/tests/browserstack_automation/config/wdio.config.js @@ -1,88 +1,90 @@ import { driver } from '@wdio/globals'; import { readFileSync } from 'fs'; -import path from 'path'; +import path, { dirname } from 'path'; import { fileURLToPath } from 'url'; -import { dirname } from 'path'; import { browserStackConfig } from './browserstack.config.js'; import { uploadAppsToBrowserStack } from '../utils/uploadAndConfigureApps.js'; -// ESM equivalent -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -//import data files +// import data files import cordovaData from '../capabilities/cordova_mobile_devices.json' with { type: 'json' }; import mobileBrowserData from '../capabilities/browser_mobile_devices.json' with { type: 'json' }; import desktopBrowserData from '../capabilities/browser_desktop.json' with { type: 'json' }; +// ESM equivalent +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + // --- Define Spec file sets const cordovaSpecs = [ - '../specs/ReadyPage.cordova.js' + '../specs/ReadyPage.cordova.js', ]; const mobileBrowserSpecs = [ - '../specs/DiscussPage.browser.js', - '../specs/FAQPage.browser.js', - '../specs/PrivacyPage.browser.js', - '../specs/ReadyPage.browser.js', - '../specs/TermsPage.browser.js', - '../specs/TopNavigation.browser.js', - '../specs/TopicsPage.browser.js', - '../specs/HowItWorks.browser.js', - '../specs/FooterLinks.browser.js', - '../specs/SignInPage.browser.js', - '../specs/BallotPage.browser.js', - '../specs/CandidatesPage.browser.js', - '../specs/VerifyCount.browser.js', - '../specs/WhosRunningForOffice.browser.js', - '../specs/CandidateDetailsPage.browser.js' + '../specs/DiscussPage.browser.js', + '../specs/FAQPage.browser.js', + '../specs/PrivacyPage.browser.js', + '../specs/ReadyPage.browser.js', + '../specs/TermsPage.browser.js', + '../specs/TopNavigation.browser.js', + '../specs/TopicsPage.browser.js', + '../specs/HowItWorks.browser.js', + '../specs/FooterLinks.browser.js', + '../specs/SignInPage.browser.js', + '../specs/BallotPage.browser.js', + '../specs/CandidatesPage.browser.js', + '../specs/VerifyCount.browser.js', + '../specs/WhosRunningForOffice.browser.js', + '../specs/CandidateDetailsPage.browser.js', + '../specs/BalletDrawerPage.browser.js', + ]; const desktopBrowserSpecs = [ - '../specs/DiscussPage.browser.js', - '../specs/FAQPage.browser.js', - '../specs/PrivacyPage.browser.js', - '../specs/ReadyPage.browser.js', - '../specs/TermsPage.browser.js', - '../specs/TopNavigation.browser.js', - '../specs/TopicsPage.browser.js', - '../specs/HowItWorks.browser.js', - '../specs/FooterLinks.browser.js', - '../specs/SignInPage.browser.js', - '../specs/BallotPage.browser.js', - '../specs/CandidatesPage.browser.js', - '../specs/VerifyCount.browser.js', - '../specs/WhosRunningForOffice.browser.js', - '../specs/CandidateDetailsPage.browser.js' + '../specs/DiscussPage.browser.js', + '../specs/FAQPage.browser.js', + '../specs/PrivacyPage.browser.js', + '../specs/ReadyPage.browser.js', + '../specs/TermsPage.browser.js', + '../specs/TopNavigation.browser.js', + '../specs/TopicsPage.browser.js', + '../specs/HowItWorks.browser.js', + '../specs/FooterLinks.browser.js', + '../specs/SignInPage.browser.js', + '../specs/BallotPage.browser.js', + '../specs/CandidatesPage.browser.js', + '../specs/VerifyCount.browser.js', + '../specs/WhosRunningForOffice.browser.js', + '../specs/CandidateDetailsPage.browser.js', + '../specs/BalletDrawerPage.browser.js', ]; -//Process Capabilities for each platform and assign specs +// Process Capabilities for each platform and assign specs // Process Cordova Capabilities (Inject App URLs) -const cordovaCapabilities = cordovaData.map(cap => { - const platform = cap.platformName?.toLowerCase(); - return { - ...cap, - 'appium:options': { - ...cap['appium:options'], - app: platform === 'android' - ? browserStackConfig.BROWSERSTACK_APK_URL - : browserStackConfig.BROWSERSTACK_IPA_URL - }, - specs: cordovaSpecs - }; +const cordovaCapabilities = cordovaData.map((cap) => { + const platform = cap.platformName?.toLowerCase(); + return { + ...cap, + 'appium:options': { + ...cap['appium:options'], + app: platform === 'android' ? + browserStackConfig.BROWSERSTACK_APK_URL : + browserStackConfig.BROWSERSTACK_IPA_URL, + }, + specs: cordovaSpecs, + }; }); -//Process mobile Browser Capabilities -const mobileBrowserCapabilities = mobileBrowserData.map(cap => ({ - ...cap, - specs: mobileBrowserSpecs +// Process mobile Browser Capabilities +const mobileBrowserCapabilities = mobileBrowserData.map((cap) => ({ + ...cap, + specs: mobileBrowserSpecs, })); -//Process desktop Capabilities -const desktopBrowserCapabilities = desktopBrowserData.map(cap => ({ - ...cap, - specs: desktopBrowserSpecs +// Process desktop Capabilities +const desktopBrowserCapabilities = desktopBrowserData.map((cap) => ({ + ...cap, + specs: desktopBrowserSpecs, })); // --- Select capabilities and assign specs based on RUN_TYPE --- @@ -112,19 +114,19 @@ const dateForDisplay = date.toDateString(); const buildName = `${browserStackConfig.NAME}: ${dateForDisplay}`; const commonOptions = { - buildName, - debug: 'true', - gpsLocation: '37.804363,-122.271111', - idleTimeout: '300', - maskCommands: 'setValues, getValues, setCookies, getCookies', - video: 'true', + buildName, + debug: 'true', + gpsLocation: '37.804363,-122.271111', + idleTimeout: '300', + maskCommands: 'setValues, getValues, setCookies, getCookies', + video: 'true', }; selectedCapabilities.forEach((capability) => { - capability['bstack:options'] = { - ...capability['bstack:options'], - ...commonOptions, - }; + capability['bstack:options'] = { + ...capability['bstack:options'], + ...commonOptions, + }; }); @@ -148,7 +150,7 @@ export const config = { specs: [], capabilities: selectedCapabilities, // onPrepare hook to display all the capabilities selected - onPrepare: async function (config, capabilities) { + async onPrepare (config, capabilities) { console.log('========== onPrepare - Starting setup for BrowserStack =========='); // Step 1: Upload apps only if required @@ -162,7 +164,7 @@ export const config = { if (ipaUrl) console.log(' IPA URL:', ipaUrl); // Step 2: Update cordova capabilities with new URLs & BrowserStack credentials - cordovaCapabilities.forEach(cap => { + cordovaCapabilities.forEach((cap) => { if (!cap['appium:options']) cap['appium:options'] = {}; if (cap.platformName.toLowerCase() === 'android') cap['appium:options'].app = apkUrl; if (cap.platformName.toLowerCase() === 'ios') cap['appium:options'].app = ipaUrl; diff --git a/tests/browserstack_automation/page_objects/ballotDrawer.browser.js b/tests/browserstack_automation/page_objects/ballotDrawer.browser.js new file mode 100644 index 000000000..2e03c24e0 --- /dev/null +++ b/tests/browserstack_automation/page_objects/ballotDrawer.browser.js @@ -0,0 +1,88 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-restricted-syntax */ +import { $, $$, driver } from '@wdio/globals'; + +import { KeyboardReturnOutlined } from '@mui/icons-material'; +import PageBrowser from './page.browser'; + +class BallotDrawer extends PageBrowser { + constructor () { + super(); + this.title = 'Ballot - WeVote'; + this.url = '/ballot'; + } + + async openPage () { + await super.open(this.url); + } + + // In WebdriverIO, this pattern is typically used within the Page Object Model (POM) to define and access UI elements efficientlyou are defining a getter method->get : + get candidateCardSelector () { + return $$('//div[contains(@class,"CandidateContainer")]'); + } + + get displayNameSelector () { + return $('(//div[contains(text(),"U.S. Representative")])[1]'); + } + + get ballotDrawer () { + return $('.MuiDrawer-paperAnchorRight'); + } + + get overlay () { + return $('.MuiModal-backdrop'); + } + + get candidateNameDrawer () { + return $('h1.OneCampaignTitle-sc-13fiky4-23'); + } + + get partyName () { + return $('.PoliticalPartyDiv-sc-14ym4n7-3'); + } + + get firstBallot () { + return $('(//div[contains(@id,\'ballotItemScrollingArea\')])[1]'); + } + + get candidateName () { + return '(//button[contains(@class,\'CandidateNameH4\')])[1]'; + } + + get candidateParty () { + return '(//div[contains(@class,\'CandidateParty\')])[1]'; + } + + get firstIssuesList () { + return '.Issues-sc-4mzi5p-1 .IssueListWrapper-sc-4mzi5p-2'; + } + + async getCandidateItems () { + // const ballotItems = await this.ballotList; + await driver.pause(10000); // Wait for the ballot items to load + const firstItem = await this.firstBallot; + const itemsData = []; + // console.log('First ballot item text:', firstItem); // Log the text of the first ballot item for debugging + + const candidateName = await firstItem.$(this.candidateName).getText(); + console.log('Candidate Name:', candidateName); // Log the candidate name for debugging + const descriptionElement = await firstItem.$(this.candidateParty).getText(); + const title = await firstItem.$$(this.firstIssuesList); + console.log('Number of issues found:', (await title.length)); // Log the number of issues found for debugging + const issueText = []; + // issue gettext() rteurns promise and in map its doent no aabout asynd await so we need to use for of loop to get the text of each issue and push it to the issueText array + // await title.map((issue) => { + // const text = issue.getText(); + // issueText.push(text); + // }); + for (const issue of title) { + const text = await issue.getText(); + issueText.push(text); + } + console.log('Issues:', issueText); + itemsData.push({ candidateName, descriptionElement, issueText }); + return itemsData; + } +} + +export default new BallotDrawer(); diff --git a/tests/browserstack_automation/specs/BalletDrawerPage.browser.js b/tests/browserstack_automation/specs/BalletDrawerPage.browser.js new file mode 100644 index 000000000..8fca87afb --- /dev/null +++ b/tests/browserstack_automation/specs/BalletDrawerPage.browser.js @@ -0,0 +1,66 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-use-before-define */ +import { driver, expect } from '@wdio/globals'; +import BallotDrawer from '../page_objects/ballotDrawer.browser'; +import ReadyPage from '../page_objects/ready.browser'; + +beforeEach(async () => { + await ReadyPage.load(); + await BallotDrawer.openPage(); + await driver.maximizeWindow(); + const element = BallotDrawer.displayNameSelector; + await driver.waitUntil( + async () => (await driver.getUrl()).includes('/ballot') && (element.isDisplayed()), + { timeout: 15000, timeoutMsg: 'Expected to be on the ballot page after 15s' }, + ); +}); +// DrawerOpen_001 +it.only('should open the drawer when user clicks button', async () => { + const cards = await BallotDrawer.candidateCardSelector; + console.log('Number of candidate cards found:', await cards.length); + cards.forEach(async (card) => { + // console.log('Checking card:', await card.getText()); + await expect(card).toBeDisplayed(); + }); + + // await cards[0].doubleClick(); + await driver.execute((el) => el.click(), cards[0]); + await driver.pause(5000); + const drawer = await BallotDrawer.ballotDrawer; + + + // Assert drawer is attached to (or very close to) right edge + + // const windowSize = await driver.getWindowSize(); + const location = await drawer.getLocation(); + const drawerRightEdge = location.x + await drawer.getSize('width'); + console.log('Drawer right edge:', drawerRightEdge); + const windowWidth = (await driver.getWindowSize()).width; + console.log('Window width:', windowWidth); + // expect(drawerRightEdge).toBeCloseTo(windowWidth, 1); + // expect(location.x).toBeGreaterThan(windowSize.width / 2 + expect(drawerRightEdge).toBeLessThanOrEqual(windowWidth); // Drawer should be on the right half of the screen + const overlay = await BallotDrawer.overlay; + const bg = await overlay.getCSSProperty('background-color'); + console.log(bg.value); // rgba(0,0,0,0.5) + expect(bg.value).toBe('rgba(0,0,0,0.5)'); // Verify the background color of the overlay +}); + +// DrawerOpen_002//DrawerOpen_002.1//DrawerOpen_002.2 + +it('should display correct candidate name and party in the drawer', async () => { + const candidatePartyElement = await BallotDrawer.candidateParty; + expect(candidatePartyElement).toBeDisplayed(); + const cards = await BallotDrawer.candidateCardSelector; + await driver.execute((el) => el.click(), cards[0]); + const cardItemsList = await BallotDrawer.getCandidateItems(); + // await (await BallotDrawer.firstBallot).click(); + const e1 = await BallotDrawer.firstBallot; + await driver.execute((el) => el.click(), cards[0]); + await driver.pause(5000); + const candidateNameElement = await BallotDrawer.candidateNameDrawer; + expect(candidateNameElement).toBeDisplayed(); + expect(cardItemsList[0].candidateName).toBe(await candidateNameElement.getText()); +}); + + From 7693d53b33f89abd29bba1972047672580772c11 Mon Sep 17 00:00:00 2001 From: Lavisha Y Date: Fri, 10 Apr 2026 20:48:05 -0700 Subject: [PATCH 2/2] config added spec --- tests/browserstack_automation/config/wdio.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/browserstack_automation/config/wdio.config.js b/tests/browserstack_automation/config/wdio.config.js index 4418a91a7..f6c17f555 100644 --- a/tests/browserstack_automation/config/wdio.config.js +++ b/tests/browserstack_automation/config/wdio.config.js @@ -37,7 +37,6 @@ const mobileBrowserSpecs = [ '../specs/WhosRunningForOffice.browser.js', '../specs/CandidateDetailsPage.browser.js', '../specs/BalletDrawerPage.browser.js', - ]; const desktopBrowserSpecs = [