Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 72 additions & 71 deletions tests/browserstack_automation/config/wdio.config.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,89 @@

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 ---
Expand Down Expand Up @@ -112,19 +113,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,
};
});


Expand All @@ -148,7 +149,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
Expand All @@ -162,7 +163,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;
Expand Down
88 changes: 88 additions & 0 deletions tests/browserstack_automation/page_objects/ballotDrawer.browser.js
Original file line number Diff line number Diff line change
@@ -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();
66 changes: 66 additions & 0 deletions tests/browserstack_automation/specs/BalletDrawerPage.browser.js
Original file line number Diff line number Diff line change
@@ -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());
});


Loading