import { Builder, By, until, Select } from 'selenium-webdriver'; import { Options as ChromeOptions } from 'selenium-webdriver/chrome.js'; import fs from 'fs'; import XLSX from 'xlsx'; import path from 'path'; const myContentWidget = (req, res) => { res.send('

ExACT Automated Test Script: DASHBOARD - My Content Widget(myContentWidget) | Initiated

'); (async function example() { let driver = await new Builder() .forBrowser('chrome') .setChromeOptions( new ChromeOptions() .addArguments('--disable-features=MediaRouter') .addArguments('--disable-extensions') .addArguments('--disable-background-networking') .addArguments('--log-level=3') ) .build(); let testStatus = 'Failed'; let workbook; let worksheet; const startTime = new Date().toISOString(); //CONSTANTS const totalItems = '5045'; //My Content Total DIV const uniqueTitleTotals = '3472'; const uniqueIdTotals = '3463'; //Biology const biologyTotalItems = '1493'; const biologyUniqueTitles = '3472'; const biologyUniqueIds = '3463'; //New Content Set const createContentSetName = 'Automated Algebra Content Set - TO BE DELETED'; //My Content sets - shared by the My Content Totals DIV table and the My Content widget list //List in default (Count) sort order, which matches the Totals table order // name = Content Set name // totalItems = Total Items (Totals table) and count (widget list) // uniqueTitles = Unique Titles (Totals table) // uniqueIds = Unique IDs (Totals table) // id = Content Set ID used in the link and edit icon URLs (null = not captured; URLs checked for format only) const myContentSets = [ { name: 'Biology', totalItems: '1493', uniqueTitles: '1482', uniqueIds: '1493', id: '585821587' }, { name: 'Khan URL test', totalItems: '1118', uniqueTitles: '41', uniqueIds: '1', id: null }, { name: 'Math Alignment Import Test', totalItems: '730', uniqueTitles: '707', uniqueIds: '728', id: '1423792414' }, { name: 'English I', totalItems: '388', uniqueTitles: '388', uniqueIds: '388', id: '585821585' }, { name: 'Copy Move Delete Test 2', totalItems: '327', uniqueTitles: '327', uniqueIds: '328', id: '1228574325' }, { name: 'Post NIght July26', totalItems: '278', uniqueTitles: '32', uniqueIds: '0', id: null }, { name: 'Algebra', totalItems: '194', uniqueTitles: '196', uniqueIds: '189', id: '585821586' }, { name: 'seesaw test', totalItems: '156', uniqueTitles: '156', uniqueIds: '155', id: null }, { name: 'US History III', totalItems: '68', uniqueTitles: '68', uniqueIds: '67', id: '585821589' }, { name: 'Import Align - Concept Based 1', totalItems: '44', uniqueTitles: '43', uniqueIds: '44', id: '1323320445' }, { name: 'Import Align - Concept Based 2', totalItems: '44', uniqueTitles: '43', uniqueIds: '44', id: '1323322624' }, { name: 'Import Align - Direct Align 1', totalItems: '44', uniqueTitles: '43', uniqueIds: '44', id: '1323322921' }, { name: 'Import Align - Direct Align 2', totalItems: '44', uniqueTitles: '43', uniqueIds: '44', id: '1323322923' }, { name: 'Copy Move Delete Test 1', totalItems: '35', uniqueTitles: '23', uniqueIds: '33', id: '1228574323' }, { name: 'ECS-3037 On Prod TC2', totalItems: '26', uniqueTitles: '26', uniqueIds: '26', id: null }, { name: 'ECS-3037 On Prod TC1', totalItems: '24', uniqueTitles: '24', uniqueIds: '24', id: null }, { name: 'create me', totalItems: '9', uniqueTitles: '9', uniqueIds: '9', id: '1427607301' }, { name: 'Illustrative Math Test 1', totalItems: '5', uniqueTitles: '5', uniqueIds: '5', id: null }, { name: 'Illustrative Math Test 2', totalItems: '5', uniqueTitles: '5', uniqueIds: '0', id: null }, { name: 'webinar test', totalItems: '5', uniqueTitles: '5', uniqueIds: '5', id: null }, { name: 'Review Screen Open', totalItems: '5', uniqueTitles: '5', uniqueIds: '0', id: null }, { name: 'create import test', totalItems: '3', uniqueTitles: '3', uniqueIds: '3', id: null }, { name: 'New One For Agust Post Night', totalItems: '0', uniqueTitles: '0', uniqueIds: '0', id: null }, ]; //My Content sets sorted by Name (Sort: "Name"), including the newly created Content Set //Sorting is case-insensitive to match the site (e.g. "create me" sorts between "Copy..." and "ECS...") //New Content Set ID is generated on create, so id is null (edit URL checked for format only) const myContentSetsByName = [ ...myContentSets, { name: createContentSetName, totalItems: '0', uniqueTitles: '0', uniqueIds: '0', id: null }, ].sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase(), 'en')); const steps = [ { description: 'Navigate to ExACT Landing Page', action: async () => await driver.navigate().to('https://correlation.edgate.com/exact/login.php') }, { description: 'Expand browser', action: async () => await driver.manage().window().maximize() }, //Login { description: 'Login', action: async () => { const multipleChecks = 'Log In'; console.log('------------->| MY CONTENT WIDGET |<-------------'); const errors = []; await driver.findElement(By.css('.form-control:nth-child(2)')).sendKeys('exact_juan'); await driver.findElement(By.css('.passfield')).sendKeys('Edgate1!'); await driver.findElement(By.css('.login')).click(); console.log('----->| NEXT ACTION: Entered credentials and logged in'); //Wait for page readyState await driver.wait(async () => { const readyState = await driver.executeScript('return document.readyState'); return readyState === 'complete'; }, 30000, 'ExACT Dashboard did not fully load within 30 seconds'); //Wait for the dashboard container to exist in DOM await driver.wait( until.elementLocated(By.css('.gap-idle-wrap > img')), 30000, 'Dashboard image did not appear within 30 seconds' ); //Wait for it to be visible await driver.wait(async () => { try { const img = await driver.findElement(By.css('.gap-idle-wrap > img')); return await img.isDisplayed(); } catch (e) { return false; } }, 30000, 'Dashboard image did not become visible within 30 seconds'); //Wait for any loading spinners to disappear await driver.wait(async () => { try { const spinners = await driver.findElements(By.css('.spinner-border')); for (const spinner of spinners) { if (await spinner.isDisplayed()) return false; } return true; } catch (e) { return true; } }, 30000, 'Loading spinners did not disappear within 30 seconds'); //Wait for the slowest resource to complete await driver.wait(async () => { const completed = await driver.executeScript(` const entries = performance.getEntriesByType('resource'); return entries.some(entry => entry.name.includes('content_sets.php') && entry.responseEnd > 0 ); `); return completed; }, 30000, 'My Content widget (content_sets.php) did not complete loading within 30 seconds'); console.log('Passed: My Content widget (content_sets.php) loaded successfully - Dashboard fully loaded'); //Final sleep to allow any remaining dynamic content to settle await driver.sleep(2000); console.log('Passed: ExACT Dashboard loaded successfully'); if (errors.length > 0) { if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } } }, //Dashboard title { description: 'Dashboard title text, size, and color', action: async () => { const multipleChecks = 'Dashboard title'; const element = await driver.findElement(By.css('.e-heading')); //Get text, font, size, and color const elementText = await element.getText(); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Correlation Dashboard') { console.log('Passed: Dashboard title text matched as expected'); } else { errors.push(`Expected: "Correlation Dashboard"; Actual: "${elementText}"`); } //font-family if (elementTextFont === `"DM Serif Display", serif`) { console.log('Passed: Dashboard title font matched as expected'); } else { errors.push(`Expected: Dashboard title font "DM Serif Display, serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '32px') { console.log('Passed: Dashboard title text font-size matched as expected'); } else { errors.push(`Expected: Dashboard title text font-size "32px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Dashboard title text color matched as expected'); } else { errors.push(`Expected: Dashboard title text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //MY CONTENT WIDGET //Dashboard My Content Widget title { description: 'My Content widget title text, color, and size', action: async () => { const multipleChecks = 'My Content widget title'; const element = await driver.findElement(By.css('.h-100 h2:nth-child(2)')); //Get text, font, size, and color const elementText = await element.getText(); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'My Content') { console.log('Passed: My Content widget title text matched as expected'); } else { errors.push(`Expected: "My Content"; Actual: "${elementText}"`); } //font-family if (elementTextFont === 'Poppins, sans-serif') { console.log('Passed: My Content widget title font matched as expected'); } else { errors.push(`Expected: My Content widget title font "Poppins, sans-serif", serif; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: My Content widget title text font-size matched as expected'); } else { errors.push(`Expected: My Content widget title text font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: My Content widget title text color matched as expected'); } else { errors.push(`Expected: My Content widget title text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content total item Count (SPECIAL CASE: with button) { description: 'My Content total item count', action: async () => { const multipleChecks = 'My Content total item count'; const element = await driver.findElement(By.css('#badge_content_total')); //Get button text, text font, text size, text color, and button color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementButtonColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText.includes(totalItems)) { console.log('Passed: Total item count button text matched as expected'); } else { errors.push(`Expected: "${totalItems}"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Total item count button text font matched as expected'); } else { errors.push(`Expected: Total item count button text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13.5px') { console.log('Passed: Total item count button text font-size matched as expected'); } else { errors.push(`Expected: Total item count button text font-size "13.5px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Total item count button text color matched as expected'); } else { errors.push(`Expected: Total item count button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //button color if (elementButtonColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Total item count button background color matched as expected'); } else { errors.push(`Expected: Total item count button background color: "rgba(40, 39, 90, 1)"; Actual: "${elementButtonColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click My Content Items Count button', action: async () => { await driver.findElement(By.css('#content_total')).click(); console.log('----->| NEXT ACTION: Clicked Total items count button'); await driver.sleep(2000); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.content_modal_title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); } }, //MY CONTENT TOTALS DIV //Dashboard My Content DIV { description: 'My Content Totals DIV title text, color, and size', action: async () => { const multipleChecks = 'My Content Totals DIV'; const element = await driver.findElement(By.css('.content_modal_title')); //Get text, font, size, and color const elementText = await element.getText(); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'My Content Totals') { console.log('Passed: My Content Totals DIV title text matched as expected'); } else { errors.push(`Expected: DIV title: "My Content Totals"; Actual: "${elementText}"`); } //font-family if (elementTextFont === 'Poppins, sans-serif') { console.log('Passed: My Content Totals DIV title font matched as expected'); } else { errors.push(`Expected: My Content Totals DIV title font "Poppins, sans-serif", serif; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: My Content Totals DIV title text font-size matched as expected'); } else { errors.push(`Expected: My Content Totals DIVt title text font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: My Content Totals DIV title text color matched as expected'); } else { errors.push(`Expected:My Content Totals DIV title text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close icon { description: 'DIV X icon color', action: async () => { const elementStyle = await driver.findElement(By.css('.ready-content span')).getCssValue('color'); if (elementStyle === 'rgba(246, 60, 40, 1)') { console.log('Passed: My Content Totals DIV X icon color matched as expected'); } else { throw new Error(`Expected: My Content Totals DIV X icon color did not match; Actual: "${elementStyle}"`); } } }, //My Content Totals DIV - Total Items { description: 'My Content Totals DIV - Total Items', action: async () => { const multipleChecks = 'My Content Total Item count'; const element = await driver.findElement(By.css('#total > p')); const elementCount = await driver.findElement(By.css('#total > b')); //Get button text, text font, text size, text color, and button color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementButtonColor = await element.getCssValue('background-color'); //Count text, text font, text size, text color const elementTotalText = await elementCount.getText(''); const elementTotalTextFont = await elementCount.getCssValue('font-family'); const elementTotalTextSize = await elementCount.getCssValue('font-size'); const elementTotalTextColor = await elementCount.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Total Items') { console.log('Passed: Total Items label matched as expected'); } else { errors.push(`Expected: "Total Items"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins") { console.log('Passed: Total Items text font matched as expected'); } else { errors.push(`Expected: Totals Items text font "Poppins"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '14px') { console.log('Passed: Total Items text font-size matched as expected'); } else { errors.push(`Expected: Totals Items text font-size "14px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Total Items label text color matched as expected'); } else { errors.push(`Expected: Totals Items text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //button color if (elementButtonColor === 'rgba(238, 238, 242, 1)') { console.log('Passed: Total Items button background color matched as expected'); } else { errors.push(`Expected: Total Items button background color: "rgba(238, 238, 242, 1)"; Actual: "${elementButtonColor}"`); } //Item count //text if (elementTotalText === totalItems) { console.log('Passed: Total Items count label matched as expected'); } else { errors.push(`Expected: Total Items: "${totalItems}"; Actual: "${elementTotalText}"`); } //font-family if (elementTotalTextFont === "Poppins, sans-serif") { console.log('Passed: Total Items count text font matched as expected'); } else { errors.push(`Expected: Totals Items count text font "Poppins, sans-serif"; Actual: ${elementTotalTextFont}`); } //font size if (elementTotalTextSize === '36px') { console.log('Passed: Total Items text font-size matched as expected'); } else { errors.push(`Expected: Totals Items count text font-size "36px"; Actual: "${elementTotalTextSize}"`); } //color if (elementTotalTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Total Items label text color matched as expected'); } else { errors.push(`Expected: Totals Items count text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTotalTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Unique Titles { description: 'My Content Totals DIV - Unique Titles', action: async () => { const multipleChecks = 'My Content Unique Titles count'; const element = await driver.findElement(By.css('#total_titles > p')); const elementCount = await driver.findElement(By.css('#total_titles > b')); //Get button text, text font, text size, text color, and button color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementButtonColor = await element.getCssValue('background-color'); //Count text, text font, text size, text color const elementTotalText = await elementCount.getText(''); const elementTotalTextFont = await elementCount.getCssValue('font-family'); const elementTotalTextSize = await elementCount.getCssValue('font-size'); const elementTotalTextColor = await elementCount.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Unique Titles') { console.log('Passed: Unique Titles label matched as expected'); } else { errors.push(`Expected: "Unique Titles"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins") { console.log('Passed: Unique Titles label text font matched as expected'); } else { errors.push(`Expected: Unique Titles text font "Poppins"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '14px') { console.log('Passed: Unique Titles text font-size matched as expected'); } else { errors.push(`Expected: Unique Titles text font-size "14px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique Titles label text color matched as expected'); } else { errors.push(`Expected: Unique Titles text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //button color if (elementButtonColor === 'rgba(238, 238, 242, 1)') { console.log('Passed: Unique Titles button background color matched as expected'); } else { errors.push(`Expected: Unique Titles button background color: "rgba(238, 238, 242, 1)"; Actual: "${elementButtonColor}"`); } //Item count //text if (elementTotalText === uniqueTitleTotals) { console.log('Passed: Unique Titles count label matched as expected'); } else { errors.push(`Expected: Unique Titles: "${uniqueTitleTotals}"; Actual: "${elementTotalText}"`); } //font-family if (elementTotalTextFont === "Poppins, sans-serif") { console.log('Passed: Unique Titles count label text font matched as expected'); } else { errors.push(`Expected: Unique Titles count text font "Poppins, sans-serif"; Actual: ${elementTotalTextFont}`); } //font size if (elementTotalTextSize === '36px') { console.log('Passed: Unique Titles text font-size matched as expected'); } else { errors.push(`Expected: Unique Titles count text font-size "36px"; Actual: "${elementTotalTextSize}"`); } //color if (elementTotalTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique Titles label text color matched as expected'); } else { errors.push(`Expected: Unique Titles count text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTotalTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Unique IDs { description: 'My Content Totals DIV - Unique IDs', action: async () => { const multipleChecks = 'My Content Unique IDs count'; const element = await driver.findElement(By.css('#total_pubtotalids > p')); const elementCount = await driver.findElement(By.css('#total_pubtotalids > b')); //Get button text, text font, text size, text color, and button color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementButtonColor = await element.getCssValue('background-color'); //Count text, text font, text size, text color const elementTotalText = await elementCount.getText(''); const elementTotalTextFont = await elementCount.getCssValue('font-family'); const elementTotalTextSize = await elementCount.getCssValue('font-size'); const elementTotalTextColor = await elementCount.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Unique IDs') { console.log('Passed: Unique IDs label matched as expected'); } else { errors.push(`Expected: "Unique IDs"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins") { console.log('Passed: Unique IDs label text font matched as expected'); } else { errors.push(`Expected: Unique IDs text font "Poppins"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '14px') { console.log('Passed: Unique IDs text font-size matched as expected'); } else { errors.push(`Expected: Unique IDs text font-size "14px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique IDs label text color matched as expected'); } else { errors.push(`Expected: Unique IDs text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //button color if (elementButtonColor === 'rgba(238, 238, 242, 1)') { console.log('Passed: Unique IDs button background color matched as expected'); } else { errors.push(`Expected: Unique IDs button background color: "rgba(238, 238, 242, 1)"; Actual: "${elementButtonColor}"`); } //Item count //text if (elementTotalText === uniqueIdTotals) { console.log('Passed: Unique IDs count label matched as expected'); } else { errors.push(`Expected: Unique IDs: "${uniqueIdTotals}"; Actual: "${elementTotalText}"`); } //font-family if (elementTotalTextFont === "Poppins, sans-serif") { console.log('Passed: Unique IDs count label text font matched as expected'); } else { errors.push(`Expected: Unique IDs count text font "Poppins, sans-serif"; Actual: ${elementTotalTextFont}`); } //font size if (elementTotalTextSize === '36px') { console.log('Passed: Unique IDs text font-size matched as expected'); } else { errors.push(`Expected: Unique IDs count text font-size "36px"; Actual: "${elementTotalTextSize}"`); } //color if (elementTotalTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique IDs label text color matched as expected'); } else { errors.push(`Expected: Unique IDs count text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTotalTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Content Set column header { description: 'My Content Totals DIV - Content Set column header', action: async () => { const multipleChecks = 'Content Set column headers'; const element = await driver.findElement(By.css('.card-body > .table-sm th:nth-child(1)')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Content Set') { console.log('Passed: Content Set column header matched as expected'); } else { errors.push(`Expected: Column header: "Content Set"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content Set column header text font matched as expected'); } else { errors.push(`Expected: Content Set column header text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '10px') { console.log('Passed: Content Set column header text font-size matched as expected'); } else { errors.push(`Expected: Content Set column header text font-size "10px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Content Set column header text color matched as expected'); } else { errors.push(`Expected: Content Set column header text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Total Items column header { description: 'My Content Totals DIV - Total Items column header', action: async () => { const multipleChecks = 'Total Items column headers'; const element = await driver.findElement(By.css('.card-body > .table-sm th:nth-child(2)')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Total Items') { console.log('Passed: Total Items column header matched as expected'); } else { errors.push(`Expected: Column header: "Total Items"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Total Items column header text font matched as expected'); } else { errors.push(`Expected: Total Items column header text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '10px') { console.log('Passed: Total Items column header text font-size matched as expected'); } else { errors.push(`Expected: Total Items column header text font-size "10px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Total Items column header text color matched as expected'); } else { errors.push(`Expected: Total Items column header text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Unique Titles column header { description: 'My Content Totals DIV - Unique Titles column header', action: async () => { const multipleChecks = 'Unique Titles column headers'; const element = await driver.findElement(By.css('.card-body > .table-sm th:nth-child(3)')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Unique Titles') { console.log('Passed: Unique Titles column header matched as expected'); } else { errors.push(`Expected: Column header: "Unique Titles"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Unique Titles column header text font matched as expected'); } else { errors.push(`Expected: Unique Titles column header text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '10px') { console.log('Passed: Unique Titles column header text font-size matched as expected'); } else { errors.push(`Expected: Unique Titles column header text font-size "10px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique Titles column header text color matched as expected'); } else { errors.push(`Expected: Unique Titles column header text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Unique IDs column header { description: 'My Content Totals DIV - Unique IDs column header', action: async () => { const multipleChecks = 'Unique IDs column headers'; const element = await driver.findElement(By.css('.card-body > .table-sm th:nth-child(4)')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Unique IDs') { console.log('Passed: Unique IDs column header matched as expected'); } else { errors.push(`Expected: Column header: "Unique IDs"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Unique IDs column header text font matched as expected'); } else { errors.push(`Expected: Unique IDs column header text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '10px') { console.log('Passed: Unique IDs column header text font-size matched as expected'); } else { errors.push(`Expected: Unique IDs column header text font-size "10px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique IDs column header text color matched as expected'); } else { errors.push(`Expected: Unique IDs column header text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content Totals DIV - Table data (Content Set, Total Items, Unique Titles, Unique IDs) { description: 'My Content Totals DIV - Table Content Set, Total Items, Unique Titles, and Unique IDs', action: async () => { const multipleChecks = 'My Content Totals table data'; //Expected rows in display order: [Content Set, Total Items, Unique Titles, Unique IDs] (from myContentSets constant) const expectedRows = myContentSets.map(s => [s.name, s.totalItems, s.uniqueTitles, s.uniqueIds]); const columns = ['Content Set', 'Total Items', 'Unique Titles', 'Unique IDs']; //Expected cell styles per column [Content Set, Total Items, Unique Titles, Unique IDs] const expectedStyles = [ { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, ]; //Wait for table rows to render await driver.wait(until.elementLocated(By.css('#content_tbody > tr')), 20000, 'My Content Totals table rows did not load within 20 seconds'); //Read all cells in one call (textContent also works for rows scrolled out of view) //Colors are normalized from rgb() to rgba() to match Selenium's getCssValue() format const cellData = await driver.executeScript(` const toRgba = c => c.startsWith('rgb(') ? c.replace('rgb(', 'rgba(').replace(')', ', 1)') : c; return Array.from(document.querySelectorAll('#content_tbody > tr')).map(tr => Array.from(tr.querySelectorAll('td')).map(td => { const cs = window.getComputedStyle(td); return { text: td.textContent.trim(), font: cs.fontFamily, size: cs.fontSize, color: toRgba(cs.color) }; }) ); `); const actualRows = cellData.map(row => row.map(cell => cell.text)); //Track all errors const errors = []; //Row count if (actualRows.length === expectedRows.length) { console.log(`Passed: My Content Totals table row count matched as expected, ${expectedRows.length}`); } else { errors.push(`Expected: ${expectedRows.length} table rows; Actual: ${actualRows.length}`); } //Cell-by-cell comparison expectedRows.forEach((expected, rowIndex) => { const actual = actualRows[rowIndex]; if (!actual) { errors.push(`Row ${rowIndex + 1} ("${expected[0]}") missing from table`); return; } const rowErrors = []; columns.forEach((column, colIndex) => { if (actual[colIndex] !== expected[colIndex]) { rowErrors.push(`${column} Expected: "${expected[colIndex]}"; Actual: "${actual[colIndex]}"`); } //Style checks (font-family, font-size, color) const cell = cellData[rowIndex][colIndex]; const style = expectedStyles[colIndex]; if (!cell) return; if (cell.font !== style.font) { rowErrors.push(`${column} font Expected: "${style.font}"; Actual: "${cell.font}"`); } if (cell.size !== style.size) { rowErrors.push(`${column} font-size Expected: "${style.size}"; Actual: "${cell.size}"`); } if (cell.color !== style.color) { rowErrors.push(`${column} color Expected: "${style.color}"; Actual: "${cell.color}"`); } }); if (rowErrors.length === 0) { console.log(`Passed: Row ${rowIndex + 1} "${expected[0]}" - ${columns.map((c, i) => `${c}: ${expected[i]}`).join(', ')} | Text font, size, and color all matched as expected`); } else { errors.push(`Row ${rowIndex + 1} ("${expected[0]}"): ${rowErrors.join('; ')}`); } }); //Report any unexpected extra rows actualRows.slice(expectedRows.length).forEach((extra, i) => { errors.push(`Unexpected row ${expectedRows.length + i + 1}: ${JSON.stringify(extra)}`); }); //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close button { description: 'Close button', action: async () => { const multipleChecks = 'Close button'; const element = await driver.findElement(By.css('#all_content_modal .btn-secondary')); //text, font, size, and color const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Close') { console.log('Passed: Close button text matched as expected'); } else { errors.push(`Expected: "Close"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Close button text font matched as expected'); } else { errors.push(`Expected: Close button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Close button text font-size matched as expected'); } else { errors.push(`Expected: Close button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Close button text color matched as expected'); } else { errors.push(`Expected: Close button text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(0, 0, 0, 0)') { console.log('Passed: Close button background color matched as expected'); } else { errors.push(`Expected: Close button background color: "rgba(0, 0, 0, 0)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Export button { description: 'Export button', action: async () => { const multipleChecks = 'Export button'; const element = await driver.findElement(By.css('#export_btn')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Export') { console.log('Passed: Export button text matched as expected'); } else { errors.push(`Expected: "Export"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Export button text font matched as expected'); } else { errors.push(`Expected: Export button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Export button text font-size matched as expected'); } else { errors.push(`Expected: Export button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Export button text color matched as expected'); } else { errors.push(`Expected: Export button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Export button background color matched as expected'); } else { errors.push(`Expected: Export button background color: "rgba(48, 200, 172, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click My Content Items Count Close button', action: async () => { await driver.findElement(By.css('.ready-content .btn-secondary')).click(); console.log('----->| NEXT ACTION: Clicked DIV Close button'); await driver.sleep(2000); } }, //Ellipsis icon color { description: 'Ellipsis icon color', action: async () => { const elementStyle = await driver.findElement(By.css('.fa-ellipsis-h')).getCssValue('color'); if (elementStyle === 'rgba(40, 39, 90, 1)') { console.log('Passed: Ellipsis icon color matched as expected'); } else { errors.push(`Expected: Ellipsis icon color did not match; Actual: "${elementStyle}"`); } } }, //Search icon color { description: 'Search icon color', action: async () => { const elementStyle = await driver.findElement(By.css('.col-12 > .fa')).getCssValue('color'); if (elementStyle === 'rgba(40, 39, 90, 1)') { console.log('Passed: Ellipsis search icon color matched as expected'); } else { errors.push(`Expected: Ellipsis search icon color did not match; Actual: "${elementStyle}"`); } } }, //Filter textbox placeholder { description: 'Filter placeholder text', action: async () => { const multipleChecks = 'Filter textbox'; const element = await driver.findElement(By.css('#content-org-filter')); //Get text, font, size, and color const elementText = await element.getAttribute('placeholder'); //Get placeholder styles via executeScript since it's a pseudo-element const elementFont = await driver.executeScript( `return window.getComputedStyle(arguments[0], '::placeholder').getPropertyValue('font-family')`, element ); const elementSize = await driver.executeScript( `return window.getComputedStyle(arguments[0], '::placeholder').getPropertyValue('font-size')`, element ); const elementColor = await driver.executeScript( `return window.getComputedStyle(arguments[0], '::placeholder').getPropertyValue('color')`, element ); //Track all errors const errors = []; //text if (elementText === 'filter...') { console.log('Passed: Filter placeholder text matched as expected'); } else { errors.push(`Expected: "filter..." placeholder; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Filter placeholder text font matched as expected'); } else { errors.push(`Expected: Filter placeholder text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '13.5px') { console.log('Passed: Filter placeholder text font-size matched as expected'); } else { errors.push(`Expected: Filter placeholder text font-size "13.5px"; Actual: "${elementSize}"`); } //color if (elementColor === 'rgb(108, 117, 125)') { console.log('Passed: Filter placeholder text color matched as expected'); } else { errors.push(`Expected: Filter placeholder text color: "rgb(108, 117, 125)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Sort label { description: 'My Sort label', action: async () => { const multipleChecks = 'Sort label'; const element = await driver.findElement(By.css('.col-4')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Sort:') { console.log('Passed: Sort: label text matched as expected'); } else { errors.push(`Expected: "Sort:" label; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Sort: label text font matched as expected'); } else { errors.push(`Expected: Sort: label text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '15px') { console.log('Passed: Sort: label text font-size matched as expected'); } else { errors.push(`Expected: Sort: label text font-size "15px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Sort: label text color matched as expected'); } else { errors.push(`Expected: Sort: label text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Sort dropdown values (SPECIAL CASE: validate default selection) { description: 'Sort dropdown values', action: async () => { const multipleChecks = 'Sort dropdown values'; const dropdown = await driver.wait(until.elementLocated(By.css('#orgsort')), 5000); //Wrap with Select class for full dropdown functionality const selectElement = new Select(dropdown); //Track all errors const errors = []; //Verify default selected value const defaultOption = await selectElement.getFirstSelectedOption(); const defaultText = await defaultOption.getText(); if (defaultText === 'Count') { console.log('Passed: Sort: dropdown default value matched as expected'); } else { errors.push(`Expected: Sort dropdown default value "Count"; Actual: "${defaultText}"`); } //Verify all dropdown options const options = await selectElement.getOptions(); const expected = [ 'Count', 'Name' ]; const actual = []; for (const opt of options) { const text = (await opt.getText()).trim(); actual.push(text); } if (JSON.stringify(actual) === JSON.stringify(expected)) { console.log('Passed: Sort: dropdown list matched as expected'); } else { errors.push(`Expected: Sort dropdown list: ${JSON.stringify(expected)}; Actual: ${JSON.stringify(actual)}`); } //Throw summary if any validation failed if (errors.length > 0) { if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } } }, //My Content - Biology { description: 'Biology link', action: async () => { const multipleChecks = 'My Content - Biology link'; const element = await driver.findElement(By.linkText('Biology')); //Get href, text, font, size, and color const elementURL = await element.getAttribute('href'); const elementText = await element.getText(); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementColor = await element.getCssValue('color'); //Track all errors const errors = []; //url if (elementURL === 'https://correlation.edgate.com/exact/?iPublisherId=585821587&function=listUnits') { console.log('Passed: Biology link URL matched as expected'); } else { errors.push(`Expected: "https://correlation.edgate.com/exact/?iPublisherId=585821587&function=listUnits"; Actual: "${elementURL}"`); } //text if (elementText === 'Biology') { console.log('Passed: Biology link text matched as expected'); } else { errors.push(`Expected: "Biology"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Biology link text font matched as expected'); } else { errors.push(`Expected: Biology link text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '14.3px') { console.log('Passed: Biology link text font-size matched as expected'); } else { errors.push(`Expected: Biology link text text font-size "14.3px"; Actual: "${elementSize}"`); } //color if (elementColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Biology link text color matched as expected'); } else { errors.push(`Expected: Biology link text color: "rgba(73, 80, 87, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content - Edit Biology icon { description: 'Edit Biology icon', action: async () => { const multipleChecks = 'My Content - Edit Biology icon'; const element = await driver.findElement(By.css('#content-org-list > li > div.orgname > a.ml-2')); //Get href, text, font, size, and color const elementURL = await element.getAttribute('href'); const elementText = await element.getAttribute('title'); const elementColor = await element.getCssValue('color'); //Track all errors const errors = []; //url if (elementURL === 'https://correlation.edgate.com/exact/contentset_edit_modal.php?id=585821587') { console.log('Passed: Edit Biology icon link URL matched as expected'); } else { errors.push(`Expected: "https://correlation.edgate.com/exact/contentset_edit_modal.php?id=585821587"; Actual: "${elementURL}"`); } //text if (elementText === 'Edit Content Set') { console.log('Passed: Edit Biology icon title matched as expected'); } else { errors.push(`Expected: "Edit Content Set" ; Actual: "${elementText}"`); } //color if (elementColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Edit Biology icon color matched as expected'); } else { errors.push(`Expected: Edit Biology icon color: "rgba(73, 80, 87, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content - List data (default sort: Count) - Content Set link, edit icon, and count { description: 'My Content list - Content Set name, link, edit icon, and count (default Count sort order)', action: async () => { const multipleChecks = 'My Content list data'; const baseURL = 'https://correlation.edgate.com/exact/'; //Expected items in display order: [Content Set, Count, Content Set ID] (from myContentSets constant) //ID null = ID not yet captured; link and edit URLs are then checked for format and matching IDs only const expectedItems = myContentSets.map(s => [s.name, s.totalItems, s.id]); //Expected styles (null = not yet confirmed; actual value is logged instead of checked) const nameStyle = { font: 'Poppins, sans-serif', size: '14.3px', color: 'rgba(73, 80, 87, 1)' }; const editIconStyle = { title: 'Edit Content Set', color: 'rgba(73, 80, 87, 1)' }; const countStyle = { font: 'Poppins, sans-serif', size: '12.87px', color: 'rgba(40, 39, 90, 1)' }; //Wait for list to render await driver.wait(until.elementLocated(By.xpath('(//ul[@id="content-org-list"])[1]/li')), 20000, 'My Content list did not load within 20 seconds'); //Read all items in one call. Each item is the nth (nested) ul#content-org-list: // li > div.orgname > a[1] = Content Set link, a[2] = edit icon; li > div > span = count //Colors are normalized from rgb() to rgba() to match Selenium's getCssValue() format const items = await driver.executeScript(` const toRgba = c => c && c.startsWith('rgb(') ? c.replace('rgb(', 'rgba(').replace(')', ', 1)') : c; const styleOf = el => { if (!el) return null; const cs = window.getComputedStyle(el); return { font: cs.fontFamily, size: cs.fontSize, color: toRgba(cs.color) }; }; return Array.from(document.querySelectorAll('ul#content-org-list')).map(ul => { const li = ul.querySelector(':scope > li'); if (!li) return null; const links = li.querySelectorAll(':scope > div.orgname > a'); const nameLink = links[0] || null; const editIcon = links[1] || null; const count = li.querySelector(':scope > div > span'); return { name: nameLink ? nameLink.textContent.trim() : null, nameHref: nameLink ? nameLink.href : null, nameStyle: styleOf(nameLink), editHref: editIcon ? editIcon.href : null, editTitle: editIcon ? editIcon.getAttribute('title') : null, editColor: editIcon ? toRgba(window.getComputedStyle(editIcon).color) : null, count: count ? count.textContent.trim() : null, countStyle: styleOf(count), }; }).filter(Boolean); `); //Track all errors const errors = []; //Item count if (items.length === expectedItems.length) { console.log(`Passed: My Content list item count matched as expected, ${expectedItems.length}`); } else { errors.push(`Expected: ${expectedItems.length} list items; Actual: ${items.length}`); } //Compare a style object against expected values (null expected = log only) const checkStyle = (label, actual, expected, itemErrors) => { if (!actual) { itemErrors.push(`${label} element not found`); return; } [['font', 'font'], ['size', 'font-size'], ['color', 'color']].forEach(([key, cssName]) => { if (expected[key] === null) return; if (actual[key] !== expected[key]) { itemErrors.push(`${label} ${cssName} Expected: "${expected[key]}"; Actual: "${actual[key]}"`); } }); }; //Item-by-item comparison expectedItems.forEach(([expName, expCount, expId], index) => { const item = items[index]; const position = index + 1; if (!item) { errors.push(`Position ${position} ("${expName}") missing from list`); return; } const itemErrors = []; //Content Set name (also validates sort order) if (item.name !== expName) { itemErrors.push(`Content Set Expected: "${expName}"; Actual: "${item.name}"`); } //Count if (item.count !== expCount) { itemErrors.push(`Count Expected: "${expCount}"; Actual: "${item.count}"`); } //Content Set link and edit icon URLs const nameId = (item.nameHref || '').match(/[?&]iPublisherId=(\d+)&function=listUnits$/); const editId = (item.editHref || '').match(/contentset_edit_modal\.php\?id=(\d+)$/); if (expId) { const expNameURL = `${baseURL}?iPublisherId=${expId}&function=listUnits`; const expEditURL = `${baseURL}contentset_edit_modal.php?id=${expId}`; if (item.nameHref !== expNameURL) { itemErrors.push(`Content Set link Expected: "${expNameURL}"; Actual: "${item.nameHref}"`); } if (item.editHref !== expEditURL) { itemErrors.push(`Edit icon link Expected: "${expEditURL}"; Actual: "${item.editHref}"`); } } else { if (!nameId || !(item.nameHref || '').startsWith(baseURL)) { itemErrors.push(`Content Set link Expected format: "${baseURL}?iPublisherId=&function=listUnits"; Actual: "${item.nameHref}"`); } if (!editId || !(item.editHref || '').startsWith(baseURL)) { itemErrors.push(`Edit icon link Expected format: "${baseURL}contentset_edit_modal.php?id="; Actual: "${item.editHref}"`); } if (nameId && editId && nameId[1] !== editId[1]) { itemErrors.push(`Content Set link ID "${nameId[1]}" does not match edit icon ID "${editId[1]}"`); } } //Edit icon title and color if (item.editTitle !== editIconStyle.title) { itemErrors.push(`Edit icon title Expected: "${editIconStyle.title}"; Actual: "${item.editTitle}"`); } if (item.editColor !== editIconStyle.color) { itemErrors.push(`Edit icon color Expected: "${editIconStyle.color}"; Actual: "${item.editColor}"`); } //Content Set link and count styles checkStyle('Content Set', item.nameStyle, nameStyle, itemErrors); checkStyle('Count', item.countStyle, countStyle, itemErrors); if (itemErrors.length === 0) { console.log(`Passed: Position ${position} "${expName}" - Item count: ${expCount}, URL, edit icon, and styles all matched as expected`); } else { errors.push(`Position ${position} ("${expName}"): ${itemErrors.join('; ')}`); } }); //Log actual count styles while countStyle expected values are not yet set if (Object.values(countStyle).some(v => v === null) && items[0] && items[0].countStyle) { console.log(`INFO: Count style not yet validated; actual (position 1): ${JSON.stringify(items[0].countStyle)}`); } //Report any unexpected extra items items.slice(expectedItems.length).forEach((extra, i) => { errors.push(`Unexpected item at position ${expectedItems.length + i + 1}: "${extra.name}" (${extra.count})`); }); //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Biology edit icon', action: async () => { await driver.findElement(By.css('#content-org-list > li > div.orgname > a.ml-2')).click(); console.log('----->| NEXT ACTION: Clicked Biology edit icon'); await driver.sleep(2000); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.modal-lg .modal-title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); } }, //Edit DIV - title { description: 'Edit DIV title', action: async () => { const multipleChecks = 'Edit DIV title'; const element = await driver.findElement(By.css('.modal-lg .modal-title')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Edit Content Set') { console.log('Passed: Edit Content Set DIV title matched as expected'); } else { errors.push(`Expected: DIV title: "Edit Content Set"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Edit Content Set DIV title font matched as expected'); } else { errors.push(`Expected: Edit Content Set DIV title font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: Edit Content Set DIV title font-size matched as expected'); } else { errors.push(`Expected: Edit Content Set DIV title font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Edit Content Set DIV title color matched as expected'); } else { errors.push(`Expected: Edit Content Set DIV title color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Edit DIV - label { description: 'Edit DIV - Content Set Name label', action: async () => { const multipleChecks = 'Edit DIV label'; const element = await driver.findElement(By.css('.modal-lg .form-label')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Content Set Name') { console.log('Passed: Content Set Name DIV label matched as expected'); } else { errors.push(`Expected: "Content Set Name"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content Set Name label font matched as expected'); } else { errors.push(`Expected: Content Set Name label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '15px') { console.log('Passed: Content Set Name label font-size matched as expected'); } else { errors.push(`Expected: Content Set Name label font-size "15px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Content Set Name label color matched as expected'); } else { errors.push(`Expected: Content Set Name label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Edit DIV - textbox { description: 'Edit DIV textbox', action: async () => { const multipleChecks = 'Edit DIV texbox'; const element = await driver.findElement(By.css('#orgname')); //Get text, font, size, and color const elementText = await element.getAttribute('value'); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Biology') { console.log('Passed: Biology textbox text matched as expected'); } else { errors.push(`Expected: "Biology"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Biology textbox font matched as expected'); } else { errors.push(`Expected: Biology textbox font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '14px') { console.log('Passed: Biology textbox font-size matched as expected'); } else { errors.push(`Expected: Biology textbox font-size "14px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Biology textbox color matched as expected'); } else { errors.push(`Expected: Biology textbox color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Delete button { description: 'Delete button', action: async () => { const multipleChecks = 'Delete button'; const element = await driver.findElement(By.css('.modal-lg #orgdelete')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementButtonColor = await element.getCssValue('background-color'); const elementStyle = await element.getAttribute('disabled'); const elementTooltip = await element.getAttribute('title'); //Track all errors const errors = []; //text if (elementText === 'Delete') { console.log('Passed: Delete button text matched as expected'); } else { errors.push(`Expected: "Delete"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Delete button text font matched as expected'); } else { errors.push(`Expected: Delete button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Delete button text font-size matched as expected'); } else { errors.push(`Expected: Delete button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Delete button text color matched as expected'); } else { errors.push(`Expected: Delete button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //button color if (elementButtonColor === 'rgba(220, 53, 69, 1)') { console.log('Passed: Delete button background color matched as expected'); } else { errors.push(`Expected: Delete button background color: "rgba(220, 53, 69, 1)"; Actual: "${elementButtonColor}"`); } //disabled if (elementStyle === 'true') { console.log('Passed: Delete button disabled as expected'); } else { errors.push(`Expected: Delete button style: "disabled"; Actual: "${elementStyle}"`); } //tooltip if (elementTooltip === 'You cannot delete a Content Set that has content items') { console.log('Passed: Delete button tool-tip matched as expected'); } else { errors.push(`Expected: Delete button tool-tip: "You cannot delete a Content Set that has content items"; Actual: "${elementTooltip}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Save button { description: 'Save button', action: async () => { const multipleChecks = 'Save button'; const element = await driver.findElement(By.css('#modalop')); //Get text, font, color, and size const elementText = await element.getAttribute('value'); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Save') { console.log('Passed: Save button text matched as expected'); } else { errors.push(`Expected: "Save"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Save button text font matched as expected'); } else { errors.push(`Expected: Save button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Save button text font-size matched as expected'); } else { errors.push(`Expected: Save button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Save button text color matched as expected'); } else { errors.push(`Expected: Save button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Save button background color matched as expected'); } else { errors.push(`Expected: Save button background color: "rgba(48, 200, 172, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close button { description: 'Close button', action: async () => { const multipleChecks = 'Close button'; const element = await driver.findElement(By.css('#modalclose')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Close') { console.log('Passed: Close button text matched as expected'); } else { errors.push(`Expected: "Close"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Close button text font matched as expected'); } else { errors.push(`Expected: Close button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Close button text font-size matched as expected'); } else { errors.push(`Expected: Close button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Close button text color matched as expected'); } else { errors.push(`Expected: Close button text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(0, 0, 0, 0)') { console.log('Passed: Close button background color matched as expected'); } else { errors.push(`Expected: Close button background color: "rgba(0, 0, 0, 0)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click X icon', action: async () => { await driver.findElement(By.css('.modal-lg .modal-header span')).click(); console.log('----->| NEXT ACTION: Clicked X icon, to close window'); await driver.sleep(2000); } }, //NEXT ACTION { description: 'Click Biology content count', action: async () => { await driver.findElement(By.css('#mycontentlist > #content-org-list > .content-org .badge')).click(); console.log('----->| NEXT ACTION: Clicked Biology 1493 content link'); await driver.sleep(2000); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.content_modal_title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); } }, //Biology Totals DIV - title { description: 'Biology Totals DIV title', action: async () => { const multipleChecks = 'Biology Totals DIV title'; const element = await driver.findElement(By.css('.content_modal_title')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Biology Totals') { console.log('Passed: Biology Totals DIV title matched as expected'); } else { errors.push(`Expected: DIV title: "Biology Totals"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Biology Totals DIV title font matched as expected'); } else { errors.push(`Expected: Biology Totals DIV title font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: Biology Totals DIV title font-size matched as expected'); } else { errors.push(`Expected: Biology Totals DIV title font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Biology Totals DIV title color matched as expected'); } else { errors.push(`Expected: Biology Totals DIV title color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Biology Total Items count 1493 { description: 'Biology DIV - Total Items count', action: async () => { const multipleChecks = 'Biology DIV Total Items count'; const element = await driver.findElement(By.css('#total > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === biologyTotalItems) { console.log(`Passed: Biology Total Items: ${biologyTotalItems} count text matched as expected`); } else { errors.push(`Expected: Biology Total Items: "${biologyTotalItems}"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Biology Total Items: Count text font matched as expected'); } else { errors.push(`Expected: Biology Total Items: Count text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '36px') { console.log('Passed: Biology Total Items: Count text font-size matched as expected'); } else { errors.push(`Expected: Biology Total Items: Count text font-size "36px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Biology Total Items: Count text color matched as expected'); } else { errors.push(`Expected: Biology Total Items: Count text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Biology Total Items (SPECIAL CASE: css path with trimmed text) { description: 'Biology DIV - Total Items', action: async () => { const multipleChecks = 'Biology DIV Total Items'; const element = await driver.findElement(By.css('#total')); //Extract the bare text node (everything that's not a tag) const elementTrimmedText = await driver.executeScript(` const el = arguments[0]; for (const node of el.childNodes) { if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) { return node.textContent.trim(); } } return null; `, element); //Get font, size, and color const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementTrimmedText === 'Total Items') { console.log('Passed: Total Items text found and matched as expected'); } else { errors.push(`Expected: Text to contain: "Total Items"; Actual: "${elementTrimmedText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Total Items text font matched as expected'); } else { errors.push(`Expected: Total Items text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Total Items text font-size matched as expected'); } else { errors.push(`Expected: Total Items text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Total Items text color matched as expected'); } else { errors.push(`Expected: Total Items text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Biology Unique Titles count 3230 { description: 'Biology DIV - Unique Titles count', action: async () => { const multipleChecks = 'Biology DIV Unique Titles count'; const element = await driver.findElement(By.css('#total_titles > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === biologyUniqueTitles) { console.log(`Passed: Biology Unique Titles: ${biologyUniqueTitles} count text matched as expected`); } else { errors.push(`Expected: Biology Unique Titles: "${biologyUniqueTitles}"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Biology Unique Titles: Count text font matched as expected'); } else { errors.push(`Expected: Biology Unique Titles: Count text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '36px') { console.log('Passed: Biology Unique Titles: Count text font-size matched as expected'); } else { errors.push(`Expected: Biology Unique Titles: Count text font-size "36px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Biology Unique Titles: Count text color matched as expected'); } else { errors.push(`Expected: Biology Unique Titles: Count text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Biology Unique Titles (SPECIAL CASE: css path with trimmed text) { description: 'Biology DIV - Unique Titles', action: async () => { const multipleChecks = 'Biology DIV Unique Titles'; const element = await driver.findElement(By.css('#total_titles')); //Extract the bare text node (everything that's not a tag) const elementTrimmedText = await driver.executeScript(` const el = arguments[0]; for (const node of el.childNodes) { if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) { return node.textContent.trim(); } } return null; `, element); //Get font, size, and color const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementTrimmedText === 'Unique Titles') { console.log('Passed: Unique Titles text found and matched as expected'); } else { errors.push(`Expected: Text to contain: "Unique Titles"; Actual: "${elementTrimmedText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Unique Titles text font matched as expected'); } else { errors.push(`Expected: v text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Unique Titles text font-size matched as expected'); } else { errors.push(`Expected: Unique Titles text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique Titles text color matched as expected'); } else { errors.push(`Expected: Unique Titles text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Biology Unique IDs count 3264 { description: 'Biology DIV - Unique IDs count', action: async () => { const multipleChecks = 'Biology DIV Unique IDs count'; const element = await driver.findElement(By.css('#total_pubtotalids > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === biologyUniqueIds) { console.log(`Passed: Biology Unique IDs: ${biologyUniqueIds} count text matched as expected`); } else { errors.push(`Expected: Biology Unique IDs: "${biologyUniqueIds}"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Biology Unique IDs: Count text font matched as expected'); } else { errors.push(`Expected: Biology Unique IDs: Count text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '36px') { console.log('Passed: Biology Unique IDs: Count text font-size matched as expected'); } else { errors.push(`Expected: Biology Unique IDs: Count text font-size "36px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Biology Unique IDs: Count text color matched as expected'); } else { errors.push(`Expected: Biology Unique IDs: Count text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Biology Unique IDs (SPECIAL CASE: css path with trimmed text) { description: 'Biology DIV - Unique IDs', action: async () => { const multipleChecks = 'Biology DIV Unique IDs'; const element = await driver.findElement(By.css('#total_pubtotalids')); //Extract the bare text node (everything that's not a tag) const elementTrimmedText = await driver.executeScript(` const el = arguments[0]; for (const node of el.childNodes) { if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) { return node.textContent.trim(); } } return null; `, element); //Get font, size, and color const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementTrimmedText === 'Unique IDs') { console.log('Passed: Unique IDss text found and matched as expected'); } else { errors.push(`Expected: Text to contain: "Unique IDs"; Actual: "${elementTrimmedText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Unique IDs text font matched as expected'); } else { errors.push(`Expected: v text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Unique IDs text font-size matched as expected'); } else { errors.push(`Expected: Unique IDs text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Unique IDs text color matched as expected'); } else { errors.push(`Expected: Unique IDs text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close button { description: 'Close button', action: async () => { const multipleChecks = 'Close button'; const element = await driver.findElement(By.css('#all_content_modal .btn-secondary')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Close') { console.log('Passed: Close button text matched as expected'); } else { errors.push(`Expected: "Close"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Close button text font matched as expected'); } else { errors.push(`Expected: Close button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Close button text font-size matched as expected'); } else { errors.push(`Expected: Close button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Close button text color matched as expected'); } else { errors.push(`Expected: Close button text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(0, 0, 0, 0)') { console.log('Passed: Close button background color matched as expected'); } else { errors.push(`Expected: Close button background color: "rgba(0, 0, 0, 0)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Export button { description: 'Export button', action: async () => { const multipleChecks = 'Export button'; const element = await driver.findElement(By.css('#export_btn')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Export') { console.log('Passed: Export button text matched as expected'); } else { errors.push(`Expected: "Export"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Export button text font matched as expected'); } else { errors.push(`Expected: Export button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Export button text font-size matched as expected'); } else { errors.push(`Expected: Export button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Export button text color matched as expected'); } else { errors.push(`Expected: Export button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Export button background color matched as expected'); } else { errors.push(`Expected: Export button background color: "rgba(48, 200, 172, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Close button', action: async () => { await driver.findElement(By.css('#all_content_modal .btn-secondary')).click(); console.log('----->| NEXT ACTION: Clicked Close button'); await driver.sleep(2000); } }, //NEXT ACTION { description: 'Click My Content ellipsis icon', action: async () => { await driver.findElement(By.css('.fa-ellipsis-h')).click(); console.log('----->| NEXT ACTION: Clicked My Content ellipsis icon'); //Wait until the element exists in the DOM const elementVisible = await driver.wait( until.elementLocated(By.css('.fa-plus')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(elementVisible), 20000 ); } }, //Create Content Set { description: 'Add Create Content Set', action: async () => { const multipleChecks = 'Add Create Content Set'; const elementIcon = await driver.findElement(By.css('.fa-plus')).getCssValue('color'); const element = await driver.findElement(By.linkText('Create Content Set')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //icon if (elementIcon === 'rgba(33, 37, 41, 1)') { console.log('Passed: Create Content Set add icon color matched as expected'); } else { errors.push(`Expected: Create Content Set add icon color: "rgba(33, 37, 41, 1)"; Actual: "${elementIcon}"`); } //text if (elementText === 'Create Content Set') { console.log('Passed: Create Content Set text matched as expected'); } else { errors.push(`Expected: "Create Content Set"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Create Content Set text font matched as expected'); } else { errors.push(`Expected: Create Content Set text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '12px') { console.log('Passed: Create Content Set text font-size matched as expected'); } else { errors.push(`Expected: Create Content Set text font-size "12px"; Actual: "${elementSize}"`); } //color if (elementTextColor === 'rgba(33, 37, 41, 1)') { console.log('Passed: Create Content Set text color matched as expected'); } else { errors.push(`Expected: Create Content Set text color: "rgba(33, 37, 41, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Export Content Set { description: 'Export Content Set', action: async () => { const multipleChecks = 'Export Content Set'; const elementIcon = await driver.findElement(By.css('.fa-download')).getCssValue('color'); const element = await driver.findElement(By.linkText('Export Content Set')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //icon if (elementIcon === 'rgba(33, 37, 41, 1)') { console.log('Passed: Export Content Set download icon color matched as expected'); } else { errors.push(`Expected: Export Content Set download icon color: "rgba(33, 37, 41, 1)"; Actual: "${elementIcon}"`); } //text if (elementText === 'Export Content Set') { console.log('Passed: Export Content Set text matched as expected'); } else { errors.push(`Expected: "Export Content Set"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Export Content Set text font matched as expected'); } else { errors.push(`Expected: Export Content Set text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '12px') { console.log('Passed: Export Content Set text font-size matched as expected'); } else { errors.push(`Expected: Export Content Set text font-size "12px"; Actual: "${elementSize}"`); } //color if (elementTextColor === 'rgba(33, 37, 41, 1)') { console.log('Passed: Export Content Set Set text color matched as expected'); } else { errors.push(`Expected: Export Content Set text color: "rgba(33, 37, 41, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Create Content Set link', action: async () => { await driver.findElement(By.linkText('Create Content Set')).click(); console.log('----->| NEXT ACTION: Clicked Create Content Set link'); //Wait for the dashboard container to exist in DOM await driver.wait( until.elementLocated(By.css('.modal-lg .modal-title')), 30000, 'DIV did not appear within 30 seconds' ); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.modal-lg .modal-title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 30000, 'DIV did not become visible within 30 seconds'); console.log('Passed: DIV loaded successfully'); } }, //Create Content Set DIV - title { description: 'Create Content Set DIV title', action: async () => { const multipleChecks = 'Create Content Set DIV title'; const element = await driver.findElement(By.css('.modal-lg .modal-title')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Create Content Set') { console.log('Passed: Create Content Set DIV title matched as expected'); } else { errors.push(`Expected: DIV title: "Create Content Set"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Create Content Set DIV title font matched as expected'); } else { errors.push(`Expected: Create Content Set DIV title font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: Create Content Set DIV title font-size matched as expected'); } else { errors.push(`Expected: Create Content Set DIV title font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Create Content Set DIV title color matched as expected'); } else { errors.push(`Expected: Create Content Set DIV title color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Content Set Name label { description: 'Content Set Name label', action: async () => { const multipleChecks = 'Content Set Name label'; const element = await driver.findElement(By.css('.form-row:nth-child(2) .form-label')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Content Set Name') { console.log('Passed: Content Set Name label matched as expected'); } else { errors.push(`Expected: Label: "Content Set Name"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content Set Name label font matched as expected'); } else { errors.push(`Expected: Content Set Name label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '15px') { console.log('Passed: Content Set Name label font-size matched as expected'); } else { errors.push(`Expected: Content Set Name label font-size "15px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Content Set Name label color matched as expected'); } else { errors.push(`Expected: Content Set Name label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Create button { description: 'Create button', action: async () => { const multipleChecks = 'Create button'; const element = await driver.findElement(By.css('.modal-lg #modalop')); //Get text, font, color, and size const elementText = await element.getAttribute('value'); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Create') { console.log('Passed: Create button text matched as expected'); } else { errors.push(`Expected: "Create"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Create button text font matched as expected'); } else { errors.push(`Expected: Create button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Create button text font-size matched as expected'); } else { errors.push(`Expected: Create button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Create button text color matched as expected'); } else { errors.push(`Expected: Create button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Create button background color matched as expected'); } else { errors.push(`Expected: Create button background color: "rgba(48, 200, 172, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close button { description: 'Close button', action: async () => { const multipleChecks = 'Close button'; const element = await driver.findElement(By.css('#modalclose')); //text, font, size, and color const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Close') { console.log('Passed: Close button text matched as expected'); } else { errors.push(`Expected: "Close"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Close button text font matched as expected'); } else { errors.push(`Expected: Close button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Close button text font-size matched as expected'); } else { errors.push(`Expected: Close button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Close button text color matched as expected'); } else { errors.push(`Expected: Close button text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(0, 0, 0, 0)') { console.log('Passed: Close button background color matched as expected'); } else { errors.push(`Expected: Close button background color: "rgba(0, 0, 0, 0)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Create button', action: async () => { await driver.findElement(By.css('.modal-lg #modalop')).click(); console.log('----->| NEXT ACTION: Clicked Create button without Content Set name'); //Wait until the element exists in the DOM const elementVisible = await driver.wait( until.elementLocated(By.css('#metawarn > b')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(elementVisible), 20000 ); await driver.sleep(2000); } }, //Validation { description: 'Empty Content Set name validation message', action: async () => { const multipleChecks = 'Empty Content Set Name validation'; const element = await driver.findElement(By.css('#metawarn > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'You must enter a Content Set name.') { console.log('Passed: Validation text matched as expected'); } else { errors.push(`Expected: Validation: "You must enter a Content Set name."; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Validation text font matched as expected'); } else { errors.push(`Expected: Validation text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Validation text font-size matched as expected'); } else { errors.push(`Expected: Validation text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(220, 53, 69, 1)') { console.log('Passed: Validation text color matched as expected'); } else { errors.push(`Expected: Validation text color: "rgba(220, 53, 69, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Enter organization name and click Create button', action: async () => { await driver.findElement(By.css('#orgname')).sendKeys('Biology'); console.log('----->| NEXT ACTION: Entered duplicate Content Set name'); await driver.sleep(2000); await driver.findElement(By.css('.modal-lg #modalop')).click(); console.log('----->| NEXT ACTION: Clicked Create button'); //Wait until the element exists in the DOM const elementVisible = await driver.wait( until.elementLocated(By.css('#namewarn')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(elementVisible), 20000 ); await driver.sleep(2000); } }, //Validation { description: 'Duplicate Content Set name validation message', action: async () => { const multipleChecks = 'Duplicate Content Set Name validation'; const element = await driver.findElement(By.css('#namewarn')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'That name is already in use. Please enter a different name.') { console.log('Passed: Validation text matched as expected'); } else { errors.push(`Expected: Validation: "That name is already in use. Please enter a different name."; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Validation text font matched as expected'); } else { errors.push(`Expected: Validation text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Validation text font-size matched as expected'); } else { errors.push(`Expected: Validation text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(220, 53, 69, 1)') { console.log('Passed: Validation text color matched as expected'); } else { errors.push(`Expected: Validation text color: "rgba(220, 53, 69, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Create New Content Set', action: async () => { const elementInput = await driver.wait(until.elementLocated(By.css('.form-row:nth-child(2) #orgname')), 5000); await elementInput.clear(); console.log('----->| NEXT ACTION: Cleared Content Set name'); //Type the deleted Content Set name to filter for it specifically for (const char of createContentSetName) { await elementInput.sendKeys(char); await driver.executeScript(`$(document.querySelector('.form-row:nth-child(2) #orgname')).trigger('keyup');`); await driver.sleep(100); } console.log('----->| NEXT ACTION: Entered new Content Set name'); await driver.findElement(By.css('.modal-lg #modalop')).click(); console.log('----->| NEXT ACTION: Clicked Create button'); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('#statalert')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); await driver.sleep(2000); } }, //Alert banner { description: 'Alert banner', action: async () => { const multipleChecks = 'Alert banner'; const element = await driver.findElement(By.css('strong')); const elementBanner = await driver.findElement(By.css('#statalert')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementBannerColor = await elementBanner.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'The content set was successfuly created') { console.log('Passed: Confirmation text matched as expected'); } else { errors.push(`Expected: Confirmation: "The content set was successfuly created"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Confirmation text font matched as expected'); } else { errors.push(`Expected: Confirmation text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Confirmation text font-size matched as expected'); } else { errors.push(`Expected: Confirmation text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Confirmation text color matched as expected'); } else { errors.push(`Expected: Confirmation text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //banner color if (elementBannerColor === 'rgba(40, 167, 69, 1)') { console.log('Passed: Confirmation banner color matched as expected'); } else { errors.push(`Expected: Confirmation banner color: "rgba(40, 167, 69, 1)"; Actual: "${elementBannerColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } //Wait until the element exists in the DOM const biologyLink = await driver.wait( until.elementLocated(By.linkText('Biology')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(biologyLink), 20000 ); //Wait for page to load completely await driver.wait(async () => { const readyState = await driver.executeScript('return document.readyState'); return readyState === 'complete'; }, 20000, 'ExACT Dashboard did not fully load within 30 seconds'); console.log('Passed: ExACT Dashboard loaded successfully'); } }, //My Content - Biology { description: 'Biology link', action: async () => { const multipleChecks = 'My Content - Biology link'; const element = await driver.findElement(By.linkText('Biology')); //Get href, text, font, size, and color const elementURL = await element.getAttribute('href'); const elementText = await element.getText(); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementColor = await element.getCssValue('color'); //Track all errors const errors = []; //url if (elementURL === 'https://correlation.edgate.com/exact/?iPublisherId=585821587&function=listUnits') { console.log('Passed: Biology link URL matched as expected'); } else { errors.push(`Expected: "https://correlation.edgate.com/exact/?iPublisherId=585821587&function=listUnits"; Actual: "${elementURL}"`); } //text if (elementText === 'Biology') { console.log('Passed: Biology link text matched as expected'); } else { errors.push(`Expected: "Biology"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Biology link text font matched as expected'); } else { errors.push(`Expected: Biology link text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '14.3px') { console.log('Passed: Biology link text font-size matched as expected'); } else { errors.push(`Expected: Biology link text text font-size "14.3px"; Actual: "${elementSize}"`); } //color if (elementColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Biology link text color matched as expected'); } else { errors.push(`Expected: Biology link text color: "rgba(73, 80, 87, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content - Edit Biology icon { description: 'Edit Biology icon', action: async () => { const multipleChecks = 'My Content - Edit Biology icon'; const element = await driver.findElement(By.css('#content-org-list > li > div.orgname > a.ml-2')); //Get href, text, font, size, and color const elementURL = await element.getAttribute('href'); const elementText = await element.getAttribute('title'); const elementColor = await element.getCssValue('color'); //Track all errors const errors = []; //url if (elementURL === 'https://correlation.edgate.com/exact/contentset_edit_modal.php?id=585821587') { console.log('Passed: Edit Biology icon link URL matched as expected'); } else { errors.push(`Expected: "https://correlation.edgate.com/exact/contentset_edit_modal.php?id=585821587"; Actual: "${elementURL}"`); } //text if (elementText === 'Edit Content Set') { console.log('Passed: Edit Biology icon title matched as expected'); } else { errors.push(`Expected: "Edit Content Set" ; Actual: "${elementText}"`); } //color if (elementColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Edit Biology icon color matched as expected'); } else { errors.push(`Expected: Edit Biology icon color: "rgba(73, 80, 87, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //My Content - Newly created content { description: 'Newly created Content Set sort order', action: async () => { const multipleChecks = `${createContentSetName} sort order`; const element = await driver.findElement(By.linkText(`${createContentSetName}`)); //Get href, text, font, size, and color const elementURL = await element.getAttribute('href'); const elementText = await element.getText(); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementColor = await element.getCssValue('color'); //Edit icon = the sibling right after the Content Set link (NOT the inner , which has no href/title) const elementEditIcon = await element.findElement(By.xpath('./following-sibling::a[@title="Edit Content Set" or contains(@href, "contentset_edit_modal.php")]')); //Get href, title, and color const elementEditIconURL = await elementEditIcon.getAttribute('href'); const elementEditIconTitle = await elementEditIcon.getAttribute('title'); const elementEditIconColor = await elementEditIcon.getCssValue('color'); //Count = badge in the same
  • as the Content Set link (no hardcoded nesting depth) const elementCount = await element.findElement(By.xpath('./ancestor::li[1]/div/span')); //Get text, font, size, and color const elementCountText = await elementCount.getText(); const elementCountFont = await elementCount.getCssValue('font-family'); const elementCountSize = await elementCount.getCssValue('font-size'); const elementCountColor = await elementCount.getCssValue('color'); //All Content Set names in list order (to confirm the new one is last) const listNames = await driver.executeScript(` return Array.from(document.querySelectorAll('ul#content-org-list > li > div.orgname > a:first-child')) .map(a => a.textContent.trim()); `); const expectedPosition = myContentSets.length + 1; //Track all errors const errors = []; //New Content Set ID is generated on create, so read it from the link and reuse it for the edit icon check const idMatch = (elementURL || '').match(/^https:\/\/correlation\.edgate\.com\/exact\/\?iPublisherId=(\d+)&function=listUnits$/); const newContentSetId = idMatch ? idMatch[1] : null; //url if (newContentSetId) { console.log(`Passed: Newly created Content Set link URL matched as expected, ID ${newContentSetId}`); } else { errors.push(`Expected: Element URL link format: "https://correlation.edgate.com/exact/?iPublisherId=&function=listUnits"; Actual: "${elementURL}"`); } //text if (elementText === createContentSetName) { console.log(`Passed: ${createContentSetName} link text matched as expected`); } else { errors.push(`Expected: "${createContentSetName}" ; Actual: "${elementText}"`); } //sort order - last place if (listNames.length === expectedPosition && listNames[listNames.length - 1] === createContentSetName) { console.log(`Passed: ${createContentSetName} listed in last place, position ${expectedPosition} as expected`); } else { errors.push(`Expected: Last place (position ${expectedPosition}): "${createContentSetName}"; Actual: position ${listNames.indexOf(createContentSetName) + 1} of ${listNames.length}, last item "${listNames[listNames.length - 1]}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Newly created Content Set link text font matched as expected'); } else { errors.push(`Expected: Newly created Content Set link text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '14.3px') { console.log('Passed: Newly created Content Set link text font-size matched as expected'); } else { errors.push(`Expected: Newly created Content Set link text font-size "14.3px"; Actual: "${elementSize}"`); } //color if (elementColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Newly created Content Set link text color matched as expected'); } else { errors.push(`Expected: Newly created Content Set link text color: "rgba(73, 80, 87, 1)"; Actual: "${elementColor}"`); } //icon title if (elementEditIconTitle === 'Edit Content Set') { console.log('Passed: Newly created Content Set edit icon title matched as expected'); } else { errors.push(`Expected: Newly created Content Set edit icon title "Edit Content Set"; Actual: "${elementEditIconTitle}"`); } //icon url - must use the same ID as the Content Set link const expectedEditIconURL = `https://correlation.edgate.com/exact/contentset_edit_modal.php?id=${newContentSetId}`; if (newContentSetId && elementEditIconURL === expectedEditIconURL) { console.log(`Passed: Newly created Content Set edit icon URL matched as expected, ID ${newContentSetId}`); } else { errors.push(`Expected: Edit icon URL: "${expectedEditIconURL}"; Actual: "${elementEditIconURL}"`); } //icon color if (elementEditIconColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Newly created Content Set edit icon color matched as expected'); } else { errors.push(`Expected: Newly created Content Set edit icon color: "rgba(73, 80, 87, 1)"; Actual: "${elementEditIconColor}"`); } //count if (elementCountText === '0') { console.log(`Passed: ${createContentSetName} content count listed in last place as expected`); } else { errors.push(`Expected: Last place: ${createContentSetName} content count: "0"; Actual: "${elementCountText}"`); } //count font-family if (elementCountFont === "Poppins, sans-serif") { console.log('Passed: Newly created Content Set content count font matched as expected'); } else { errors.push(`Expected: Newly created Content Set content count font "Poppins, sans-serif"; Actual: ${elementCountFont}`); } //count font size if (elementCountSize === '12.87px') { console.log('Passed: Newly created Content Set content count font-size matched as expected'); } else { errors.push(`Expected: Newly created Content Set content count font-size "12.87px"; Actual: "${elementCountSize}"`); } //count color if (elementCountColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Newly created Content Set content count color matched as expected'); } else { errors.push(`Expected: Newly created Content Set content count color: "rgba(40, 39, 90, 1)"; Actual: "${elementCountColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click newly created Content Set link', action: async () => { await driver.findElement(By.linkText(`${createContentSetName}`)).click(); console.log(`----->| NEXT ACTION: Clicked ${createContentSetName} link`); //Wait until the element exists in the DOM const elementVisible = await driver.wait( until.elementLocated(By.css('.well > b')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(elementVisible), 20000 ); } }, //Filter Results label { description: 'Filter Results label', action: async () => { const multipleChecks = 'Filter Results label'; const element = await driver.findElement(By.css('.well > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Filter Results') { console.log('Passed: Filter Results label matched as expected'); } else { errors.push(`Expected: Label: "Filter Results"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Filter Results label font matched as expected'); } else { errors.push(`Expected: Filter Results label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Filter Results label font-size matched as expected'); } else { errors.push(`Expected: Filter Results label font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Filter Results label color matched as expected'); } else { errors.push(`Expected: Filter Results label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Grade label { description: 'Grade label', action: async () => { const multipleChecks = 'Grade label'; const element = await driver.findElement(By.css('.form-group:nth-child(1) > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Grade') { console.log('Passed: Grade label matched as expected'); } else { errors.push(`Expected: Label: "Grade"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Grade label font matched as expected'); } else { errors.push(`Expected: Grade label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Grade label font-size matched as expected'); } else { errors.push(`Expected: Grade label font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Grade label color matched as expected'); } else { errors.push(`Expected: Grade label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Subject label { description: 'Subject label', action: async () => { const multipleChecks = 'Subject label'; const element = await driver.findElement(By.css('.form-group:nth-child(3) > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Subject') { console.log('Passed: Subject label matched as expected'); } else { errors.push(`Expected: Label: "Subject"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Subject label font matched as expected'); } else { errors.push(`Expected: Subject label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Subject label font-size matched as expected'); } else { errors.push(`Expected: Subject label font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Subject label color matched as expected'); } else { errors.push(`Expected: Subject label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Reset All link { description: 'Reset All link', action: async () => { const multipleChecks = 'Reset All link'; const element = await driver.findElement(By.linkText('Reset All')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Reset All') { console.log('Passed: Reset All link matched as expected'); } else { errors.push(`Expected: Link: "Reset All"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Reset All link font matched as expected'); } else { errors.push(`Expected: Reset All link font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Reset All link font-size matched as expected'); } else { errors.push(`Expected: Reset All link font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Reset All link color matched as expected'); } else { errors.push(`Expected: Reset All link color: "rgba(73, 80, 87, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Home link { description: 'Home link', action: async () => { const multipleChecks = 'Home link'; const element = await driver.findElement(By.linkText('Home')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Home') { console.log('Passed: Home link matched as expected'); } else { errors.push(`Expected: Link: "Home"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Home link font matched as expected'); } else { errors.push(`Expected: Home link font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Home link font-size matched as expected'); } else { errors.push(`Expected: Home link font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Home link color matched as expected'); } else { errors.push(`Expected: Home link color: "rgba(73, 80, 87, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Content link { description: 'Content link', action: async () => { const multipleChecks = 'Content link'; const element = await driver.findElement(By.css('.breadcrumb-item:nth-child(2)')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Content') { console.log('Passed: Content link matched as expected'); } else { errors.push(`Expected: Link: "Content"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content link font matched as expected'); } else { errors.push(`Expected: Content link font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Content link font-size matched as expected'); } else { errors.push(`Expected: Content link font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Content link color matched as expected'); } else { errors.push(`Expected: Content link color: "rgba(48, 200, 172, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Results text { description: 'Results text', action: async () => { const multipleChecks = 'Results text'; const element = await driver.findElement(By.css('b:nth-child(2)')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'There are no items that match your search criteria.') { console.log('Passed: Results text matched as expected'); } else { errors.push(`Expected: "There are no items that match your search criteria."; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Results text link font matched as expected'); } else { errors.push(`Expected: Results text link font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Results text link font-size matched as expected'); } else { errors.push(`Expected: Results text link font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Results text link color matched as expected'); } else { errors.push(`Expected: Results text link color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Dashboard navigation link', action: async () => { await driver.findElement(By.linkText('Dashboard')).click(); console.log(`----->| NEXT ACTION: Clicked Dashboard navigation link`); //Wait until the element exists in the DOM const correlationCoveragesImage = await driver.wait( until.elementLocated(By.css('.gap-idle-wrap > img')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(correlationCoveragesImage), 20000 ); //Wait for page to load completely await driver.wait(async () => { const readyState = await driver.executeScript('return document.readyState'); return readyState === 'complete'; }, 20000, 'ExACT Dashboard did not fully load within 30 seconds'); console.log('Passed: ExACT Dashboard loaded successfully'); } }, //NEXT ACTION { description: 'Click My Content ellipsis icon', action: async () => { await driver.findElement(By.css('.fa-ellipsis-h')).click(); //Wait until the element exists in the DOM const elementVisible = await driver.wait( until.elementLocated(By.css('.fa-plus')), 20000 ); await driver.sleep(1000); //Wait until it becomes visible await driver.wait( until.elementIsVisible(elementVisible), 20000 ); await driver.sleep(1000); await driver.findElement(By.linkText('Export Content Set')).click(); console.log('----->| NEXT ACTION: Clicked My Content ellipsis icon and Export Content Set link'); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.modal-header:nth-child(5) > .modal-title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); } }, //Export Content DIV { description: 'Export Content DIV', action: async () => { const multipleChecks = 'Export Content DIV'; const element = await driver.findElement(By.css('.modal-header:nth-child(5) > .modal-title')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Export Content') { console.log('Passed: Export Content DIV title matched as expected'); } else { errors.push(`Expected: DIV title: "Export Content"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Export Content DIV title font matched as expected'); } else { errors.push(`Expected: Export Content DIV title font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: Export Content DIV title font-size matched as expected'); } else { errors.push(`Expected: Export Content DIV title font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(48, 48, 96, 1)') { console.log('Passed: Export Content DIV title color matched as expected'); } else { errors.push(`Expected: Export Content DIV title color: "rgba(48, 48, 96, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Export Content DIV label { description: 'Export Content label', action: async () => { const multipleChecks = 'Export Content label'; const element = await driver.findElement(By.css('#content-export-container > h5')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Please select one or more content sets to export:') { console.log('Passed: Export Content DIV label matched as expected'); } else { errors.push(`Expected: DIV label: "Please select one or more content sets to export:"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Export Content DIV label font matched as expected'); } else { errors.push(`Expected: Results text label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '20px') { console.log('Passed: Export Content DIV label font-size matched as expected'); } else { errors.push(`Expected: Export Content DIV label font-size "20px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Export Content DIV label color matched as expected'); } else { errors.push(`Expected: Export Content DIV label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Content Set label { description: 'Content Set label', action: async () => { const multipleChecks = 'Content Set label'; const element = await driver.findElement(By.css('#content_export .form-label')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Content Set') { console.log('Passed: Content Set label matched as expected'); } else { errors.push(`Expected: Label: "Content Set"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content Set label font matched as expected'); } else { errors.push(`Expected: Content Set label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '15px') { console.log('Passed: Content Set label font-size matched as expected'); } else { errors.push(`Expected: Content Set label font-size "15px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(48, 48, 96, 1)') { console.log('Passed: Content Set label color matched as expected'); } else { errors.push(`Expected: Content Set label color: "rgba(48, 48, 96, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Content Set dropdown value { description: 'Content Set dropdown value', action: async () => { const multipleChecks = 'Content Set dropdown value'; const element = await driver.findElement(By.css('.form-group:nth-child(1) .multiselect-selected-text')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementTitle = await driver.findElement(By.css('.form-group:nth-child(1) .multiselect')).getAttribute('title'); //Track all errors const errors = []; //text if (elementText === 'Please select a Content Set') { console.log('Passed: Content Set dropdown value matched as expected'); } else { errors.push(`Expected: "Please select a Content Set"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content Set dropdown value font matched as expected'); } else { errors.push(`Expected: Content Set dropdown value font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '16px') { console.log('Passed: Content Set dropdown value font-size matched as expected'); } else { errors.push(`Expected: Content Set dropdown value font-size "16px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Content Set dropdown value color matched as expected'); } else { errors.push(`Expected: Content Set dropdown value color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //title if (elementTitle === 'Please select a Content Set') { console.log('Passed: Content Set dropdown value title matched as expected'); } else { errors.push(`Expected: Content Set dropdown value title: "Please select a Content Set"; Actual: "${elementTitle}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Export button { description: 'Export button', action: async () => { const multipleChecks = 'Export button'; const element = await driver.findElement(By.css('#exportButton')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Export') { console.log('Passed: Export button text matched as expected'); } else { errors.push(`Expected: "Export"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Export button text font matched as expected'); } else { errors.push(`Expected: Export button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '15px') { console.log('Passed: Export button text font-size matched as expected'); } else { errors.push(`Expected: Export button text font-size "15px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Export button text color matched as expected'); } else { errors.push(`Expected: Export button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Export button background color matched as expected'); } else { errors.push(`Expected: Export button background color: "rgba(48, 200, 172, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close button { description: 'Close button', action: async () => { const multipleChecks = 'Close button'; const element = await driver.findElement(By.css('.btn:nth-child(3)')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Close') { console.log('Passed: Close button text matched as expected'); } else { errors.push(`Expected: "Close"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Close button text font matched as expected'); } else { errors.push(`Expected: Close button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '15px') { console.log('Passed: Close button text font-size matched as expected'); } else { errors.push(`Expected: Close button text font-size "15px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Close button text color matched as expected'); } else { errors.push(`Expected: Close button text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(0, 0, 0, 0)') { console.log('Passed: Close button background color matched as expected'); } else { errors.push(`Expected: Close button background color: "rgba(0, 0, 0, 0)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Export DIV alert message', action: async () => { const multipleChecks = 'Export DIV alert'; const errors = []; //Wait for modal to be ready await driver.wait(until.elementLocated(By.css('.modal-header:nth-child(5) > .modal-title')), 5000); //Click export button await driver.wait(until.elementLocated(By.css('#exportButton')), 5000).click(); await driver.sleep(2500); console.log('----->| NEXT ACTION: Clicked Export button'); //Wait for confirm dialog to appear const alert = await driver.wait(until.alertIsPresent(), 5000); console.log('----->| NEXT ACTION: Alert dialog appeared'); //Get alert text const alertText = await alert.getText(); //Validate alert message text const expectedText = 'Please select at least one Content Set.'; if (alertText === expectedText) { console.log('Passed: Export alert message text matched as expected'); } else { errors.push(`Expected: "${expectedText}"; Actual: "${alertText}"`); } //Throw summary before dismissing if errors found if (errors.length > 0) { await alert.dismiss(); //Cancel the export if validation failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } //Accept the alert (click OK) to proceed with export await alert.accept(); console.log('----->| NEXT ACTION: Clicked OK on alert DIV'); await driver.sleep(2000); } }, //Export - Search textbox placeholder { description: 'Export Search placeholder text', action: async () => { const multipleChecks = 'Export Search textbox'; //Click to expand dropdown list await driver.findElement(By.css('#content_export > div > div > button > span')).click(); console.log('----->| NEXT ACTION: Clicked Content Set dropdown'); await driver.sleep(2000); const elementIcon = await driver.findElement(By.css('.show .input-group-prepend img')).getCssValue('color'); const element = await driver.findElement(By.css('.show > .multiselect-item .form-control')); const elementClearIcon = await driver.findElement(By.css('.form-group:nth-child(1) .input-group-append img')).getCssValue('color'); //Get placeholder text, size, and color const elementText = await element.getAttribute('placeholder'); //Get placeholder styles via executeScript since it's a pseudo-element const elementFont = await driver.executeScript( `return window.getComputedStyle(arguments[0], '::placeholder').getPropertyValue('font-family')`, element ); const elementSize = await driver.executeScript( `return window.getComputedStyle(arguments[0], '::placeholder').getPropertyValue('font-size')`, element ); const elementColor = await driver.executeScript( `return window.getComputedStyle(arguments[0], '::placeholder').getPropertyValue('color')`, element ); //Track all errors const errors = []; //Search icon color if (elementIcon === 'rgba(169, 169, 189, 1)') { console.log('Passed: Search icon color matched as expected'); } else { errors.push(`Expected: Search icon color: "rgba(169, 169, 189, 1)"; Actual: "${elementIcon}"`); } //text if (elementText === 'Search') { console.log('Passed: Search placeholder text matched as expected'); } else { errors.push(`Expected: "Search" placeholder; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Search placeholder text font matched as expected'); } else { errors.push(`Expected: Search placeholder text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '13.5px') { console.log('Passed: Search placeholder text font-size matched as expected'); } else { errors.push(`Expected: Search placeholder text font-size "13.5px"; Actual: "${elementSize}"`); } //color if (elementColor === 'rgb(108, 117, 125)') { console.log('Passed: Search placeholder text color matched as expected'); } else { errors.push(`Expected: Search placeholder text color: "rgb(108, 117, 125)"; Actual: "${elementColor}"`); } //Clear icon color if (elementClearIcon === 'rgba(255, 59, 48, 1)') { console.log('Passed: Search clear icon color matched as expected'); } else { errors.push(`Expected: Search clear icon color: "rgba(255, 59, 48, 1)"; Actual: "${elementClearIcon}"`); } //Throw summary if any validation failed if (errors.length > 0) { if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } } }, //Sort dropdown values (SPECIAL CASE: multi-selection) { description: 'Export multiselect dropdown list', action: async () => { const multipleChecks = 'Export multiselect dropdown list'; //Track all errors// const errors = []; //Target the correct dropdown directly by its unique id const dropdown = await driver.wait(until.elementLocated(By.css('#selectOrg')), 5000); const selectElement = new Select(dropdown); //Get all options const options = await selectElement.getOptions(); //console.log('----->| Total options found:', options.length); const expected = [ 'US History III', 'English I', 'Algebra', 'Biology', 'Copy Move Delete Test 1', 'Copy Move Delete Test 2', 'Import Align - Concept Based 1', 'Import Align - Concept Based 2', 'Import Align - Direct Align 1', 'Import Align - Direct Align 2', 'Math Alignment Import Test', 'create me', 'Illustrative Math Test 1', 'Illustrative Math Test 2', 'Khan URL test', 'seesaw test', 'ECS-3037 On Prod TC2', 'ECS-3037 On Prod TC1', 'webinar test', 'Review Screen Open', 'New One For Agust Post Night', 'Automated Algebra Content Set - TO BE DELETED', 'Post NIght July26', 'create import test' ]; const actual = []; for (const opt of options) { const text = await driver.executeScript(` return arguments[0].textContent.trim(); `, opt); if (text !== '') { actual.push(text); //console.log(`----->| Option: "${text}"`); } } //console.log('----->| DEBUG actual:', JSON.stringify(actual)); //Validate list matches expected if (JSON.stringify(actual) === JSON.stringify(expected)) { console.log('Passed: Export Content multi-select dropdown list', options.length + ' matched as expected'); } else { errors.push(`Expected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(actual)}`); } //Throw summary if any validation failed if (errors.length > 0) { if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click filter Content Set list and select newly created Content Set', action: async () => { const elementInput = await driver.wait(until.elementLocated(By.css('.show > .multiselect-item .form-control')), 5000); for (const char of 'Auto') { await elementInput.sendKeys(char); await driver.executeScript(`$(document.querySelector('.show > .multiselect-item .form-control')).trigger('keyup');`); await driver.sleep(100); } console.log('----->| NEXT ACTION: Entered search term - Auto'); await driver.findElement(By.css('li:nth-child(24) input')).click(); console.log(`----->| NEXT ACTION: Clicked checkbox for: ${createContentSetName}`); await driver.sleep(2000); await driver.findElement(By.css('.modal-header:nth-child(5)')).click(); console.log('----->| NEXT ACTION: Clicked modal to close dropdown list'); await driver.sleep(2000); await driver.findElement(By.css('#exportButton')).click(); console.log('----->| NEXT ACTION: Clicked Export button'); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('h2')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); } }, //Export page { description: 'Export page title', action: async () => { const multipleChecks = 'Export page title'; const element = await driver.findElement(By.css('h2')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Export') { console.log('Passed: Export title matched as expected'); } else { errors.push(`Expected: DIV title: "Export"; Actual: "${elementText}"`); } //font-family if (elementTextFont === `"DM Serif Display", serif`) { console.log('Passed: Export title font matched as expected'); } else { errors.push(`Expected: Export title font "DM Serif Display, serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '32px') { console.log('Passed: Export title font-size matched as expected'); } else { errors.push(`Expected: Export title font-size "32px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Export title color matched as expected'); } else { errors.push(`Expected: Export title color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Export page label { description: 'Export page label', action: async () => { const multipleChecks = 'Export page label'; const element = await driver.findElement(By.css('body > div.container.body > div > div.right_col > div > b')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Export complete!') { console.log('Passed: Export label matched as expected'); } else { errors.push(`Expected: DIV label: "Export complete!"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Export label font matched as expected'); } else { errors.push(`Expected: Export label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Export label font-size matched as expected'); } else { errors.push(`Expected: Export label font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Export label color matched as expected'); } else { errors.push(`Expected: Export label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Download Export button { description: 'Download Export button', action: async () => { const multipleChecks = 'Download Export button'; const elementIcon = await driver.wait(until.elementLocated(By.css('.fa-download')), 5000).catch(() => false); const element = await driver.findElement(By.linkText('Download Export')); //Get icon, text, color, and size const elementText = await element.getText(); const elementFont = await element.getCssValue('font-family'); const elementColor = await element.getCssValue('color'); const elementSize = await element.getCssValue('font-size'); //Track all errors const errors = []; //icon if (elementIcon) { console.log('Passed: Download Export button icon present'); } else { errors.push('Download Export button icon not found'); } //text if (elementText === 'Download Export') { console.log('Passed: Download Export button text matched as expected'); } else { errors.push(`Expected: "Download Export"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Download Export button text font matched as expected'); } else { errors.push(`Expected: Download Export button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //color if (elementColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Download Export button text color matched as expected'); } else { errors.push(`Expected: Download Export button text color "rgba(255, 255, 255, 1)"; Actual: "${elementColor}"`); } //font size if (elementSize === '20px') { console.log('Passed: Download Export button text font-size matched as expected'); } else { errors.push(`Expected: Download Export button text text font-size "20px"; Actual: "${elementSize}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click Dashboard navigation link', action: async () => { await driver.findElement(By.linkText('Dashboard')).click(); console.log(`----->| NEXT ACTION: Clicked Dashboard navigation link`); //Wait until the element exists in the DOM const correlationCoveragesImage = await driver.wait( until.elementLocated(By.css('.gap-idle-wrap > img')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(correlationCoveragesImage), 20000 ); //Wait for page to load completely await driver.wait(async () => { const readyState = await driver.executeScript('return document.readyState'); return readyState === 'complete'; }, 20000, 'ExACT Dashboard did not fully load within 30 seconds'); console.log('Passed: ExACT Dashboard loaded successfully'); } }, //My Content - Sort by Count (default) { description: 'Newly created Content Set sort order', action: async () => { const multipleChecks = `${createContentSetName} sort order`; const element = await driver.findElement(By.linkText(`${createContentSetName}`)); //Get href, text, font, size, and color const elementURL = await element.getAttribute('href'); const elementText = await element.getText(); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementColor = await element.getCssValue('color'); //Edit icon = the sibling right after the Content Set link (NOT the inner , which has no href/title) const elementEditIcon = await element.findElement(By.xpath('./following-sibling::a[@title="Edit Content Set" or contains(@href, "contentset_edit_modal.php")]')); //Get href, title, and color const elementEditIconURL = await elementEditIcon.getAttribute('href'); const elementEditIconTitle = await elementEditIcon.getAttribute('title'); const elementEditIconColor = await elementEditIcon.getCssValue('color'); //Count = badge in the same
  • as the Content Set link (no hardcoded nesting depth) const elementCount = await element.findElement(By.xpath('./ancestor::li[1]/div/span')); //Get text, font, size, and color const elementCountText = await elementCount.getText(); const elementCountFont = await elementCount.getCssValue('font-family'); const elementCountSize = await elementCount.getCssValue('font-size'); const elementCountColor = await elementCount.getCssValue('color'); //All Content Set names in list order (to confirm the new one is last) const listNames = await driver.executeScript(` return Array.from(document.querySelectorAll('ul#content-org-list > li > div.orgname > a:first-child')) .map(a => a.textContent.trim()); `); const expectedPosition = myContentSets.length + 1; //Track all errors const errors = []; //New Content Set ID is generated on create, so read it from the link and reuse it for the edit icon check const idMatch = (elementURL || '').match(/^https:\/\/correlation\.edgate\.com\/exact\/\?iPublisherId=(\d+)&function=listUnits$/); const newContentSetId = idMatch ? idMatch[1] : null; //url if (newContentSetId) { console.log(`Passed: Newly created Content Set link URL matched as expected, ID ${newContentSetId}`); } else { errors.push(`Expected: Element URL link format: "https://correlation.edgate.com/exact/?iPublisherId=&function=listUnits"; Actual: "${elementURL}"`); } //text if (elementText === createContentSetName) { console.log(`Passed: ${createContentSetName} link text matched as expected`); } else { errors.push(`Expected: "${createContentSetName}" ; Actual: "${elementText}"`); } //sort order - last place if (listNames.length === expectedPosition && listNames[listNames.length - 1] === createContentSetName) { console.log(`Passed: ${createContentSetName} listed in last place, position ${expectedPosition} as expected`); } else { errors.push(`Expected: Last place (position ${expectedPosition}): "${createContentSetName}"; Actual: position ${listNames.indexOf(createContentSetName) + 1} of ${listNames.length}, last item "${listNames[listNames.length - 1]}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Newly created Content Set link text font matched as expected'); } else { errors.push(`Expected: Newly created Content Set link text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '14.3px') { console.log('Passed: Newly created Content Set link text font-size matched as expected'); } else { errors.push(`Expected: Newly created Content Set link text font-size "14.3px"; Actual: "${elementSize}"`); } //color if (elementColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Newly created Content Set link text color matched as expected'); } else { errors.push(`Expected: Newly created Content Set link text color: "rgba(73, 80, 87, 1)"; Actual: "${elementColor}"`); } //icon title if (elementEditIconTitle === 'Edit Content Set') { console.log('Passed: Newly created Content Set edit icon title matched as expected'); } else { errors.push(`Expected: Newly created Content Set edit icon title "Edit Content Set"; Actual: "${elementEditIconTitle}"`); } //icon url - must use the same ID as the Content Set link const expectedEditIconURL = `https://correlation.edgate.com/exact/contentset_edit_modal.php?id=${newContentSetId}`; if (newContentSetId && elementEditIconURL === expectedEditIconURL) { console.log(`Passed: Newly created Content Set edit icon URL matched as expected, ID ${newContentSetId}`); } else { errors.push(`Expected: Edit icon URL: "${expectedEditIconURL}"; Actual: "${elementEditIconURL}"`); } //icon color if (elementEditIconColor === 'rgba(73, 80, 87, 1)') { console.log('Passed: Newly created Content Set edit icon color matched as expected'); } else { errors.push(`Expected: Newly created Content Set edit icon color: "rgba(73, 80, 87, 1)"; Actual: "${elementEditIconColor}"`); } //count if (elementCountText === '0') { console.log(`Passed: ${createContentSetName} content count listed in last place as expected`); } else { errors.push(`Expected: Last place: ${createContentSetName} content count: "0"; Actual: "${elementCountText}"`); } //count font-family if (elementCountFont === "Poppins, sans-serif") { console.log('Passed: Newly created Content Set content count font matched as expected'); } else { errors.push(`Expected: Newly created Content Set content count font "Poppins, sans-serif"; Actual: ${elementCountFont}`); } //count font size if (elementCountSize === '12.87px') { console.log('Passed: Newly created Content Set content count font-size matched as expected'); } else { errors.push(`Expected: Newly created Content Set content count font-size "12.87px"; Actual: "${elementCountSize}"`); } //count color if (elementCountColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Newly created Content Set content count color matched as expected'); } else { errors.push(`Expected: Newly created Content Set content count color: "rgba(40, 39, 90, 1)"; Actual: "${elementCountColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Filter My Content list', action: async () => { const filterInput = await driver.findElement(By.css('#content-org-filter')); await filterInput.click(); await driver.sleep(500); for (const char of 'algebra') { await filterInput.sendKeys(char); await driver.executeScript(`$(document.querySelector('#content-org-filter')).trigger('keyup');`); await driver.sleep(100); } console.log('----->| NEXT ACTION: Filtered My Content list by - algebra'); await driver.sleep(2000); //Wait for visible items to reduce await driver.wait(async () => { try { const visibleCount = await driver.executeScript(` const all = document.querySelectorAll('#mycontentlist .orgname'); return Array.from(all).filter(el => { const style = window.getComputedStyle(el.closest('li')); return style.display !== 'none' && style.visibility !== 'hidden'; }).length; `); return visibleCount < 13; } catch (e) { return false; } }, 10000, 'List did not filter within 10 seconds'); console.log('Passed: Filter "algebra" applied successfully'); } }, //Validate sort order by filter algebra { description: 'Sort order by filter algebra', action: async () => { const multipleChecks = 'My Content - Sort order by filter algebra'; const errors = []; const rows = await driver.executeScript(` const results = []; const items = document.querySelectorAll('#mycontentlist .orgname'); Array.from(items).forEach((item) => { //Only include visible items const li = item.closest('li'); const style = window.getComputedStyle(li); if (style.display === 'none' || style.visibility === 'hidden') return; const nameLink = item.querySelector('a:first-child'); const name = nameLink ? nameLink.textContent.trim() : ''; const editLink = item.querySelector('a.ml-2'); const href = editLink ? editLink.getAttribute('href') : ''; const countSpan = li.querySelector('.badge.contnet_set_count'); const count = countSpan ? countSpan.textContent.trim() : ''; results.push({ position: results.length + 1, name, href, count }); }); return results; `); //Positions are now 1 and 2 since we only have 2 visible items const expected = [ { position: 1, name: 'Algebra', count: '194', href: '/exact/contentset_edit_modal.php?id=585821586' }, { position: 2, name: 'Automated Algebra Content Set - TO BE DELETED', count: '0', hrefContains: '/exact/contentset_edit_modal.php?id=' } ]; for (const exp of expected) { const actual = rows[exp.position - 1]; if (!actual) { errors.push(`Position ${exp.position}: row not found`); continue; } const nameMatch = actual.name === exp.name; const countMatch = actual.count === exp.count; //Use includes() for href if exp.hrefContains is defined, otherwise exact match const hrefMatch = exp.hrefContains ? actual.href.includes(exp.hrefContains) : actual.href === exp.href; if (nameMatch && countMatch && hrefMatch) { console.log(`Passed: Sort Order ${exp.position} - ${exp.name} | Count: ${exp.count} | href | All matched as expected`); } else { if (!nameMatch) errors.push(`Sort Order ${exp.position} name - Expected: "${exp.name}"; Actual: "${actual.name}"`); if (!countMatch) errors.push(`Sort Order ${exp.position} count - Expected: "${exp.count}"; Actual: "${actual.count}"`); if (!hrefMatch) errors.push(`Sort Order ${exp.position} href - Expected: "${exp.href}"; Actual: "${actual.href}"`); } } //Throw summary if any validation failed if (errors.length > 0) { if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } } }, //NEXT ACTION orgsort { description: 'Click Name sort by option', action: async () => { //Use Select class for reliable dropdown interaction const sortDropdown = await driver.wait(until.elementLocated(By.css('#orgsort')), 5000); const selectElement = new Select(sortDropdown); await selectElement.selectByVisibleText('Name'); console.log('----->| NEXT ACTION: Selected Sort: "Name" option'); //Wait specifically for the list to re-render with the first Name-sorted Content Set first const expectedFirst = myContentSetsByName[0].name; await driver.wait(async () => { try { const firstItem = await driver.executeScript(` const first = document.querySelector('#mycontentlist .orgname a:first-child'); return first ? first.textContent.trim() : ''; `); return firstItem === expectedFirst; } catch (e) { return false; } }, 10000, 'List did not re-sort by Name within 10 seconds'); console.log('Passed: Sort by Name applied successfully'); } }, //Sort by Name { description: 'Sort order by Name', action: async () => { const multipleChecks = 'My Content - Sort order by Name'; const errors = []; const rows = await driver.executeScript(` const results = []; const items = document.querySelectorAll('#mycontentlist .orgname'); items.forEach((item, index) => { const nameLink = item.querySelector('a:first-child'); const name = nameLink ? nameLink.textContent.trim() : ''; const editLink = item.querySelector('a.ml-2'); const href = editLink ? editLink.getAttribute('href') : ''; const countSpan = item.closest('li').querySelector('.badge.contnet_set_count'); const count = countSpan ? countSpan.textContent.trim() : ''; results.push({ position: index + 1, name, href, count }); }); return results; `); //Expected Name sort order built from the myContentSets constant (+ newly created Content Set) //Known IDs check the exact edit URL; null IDs (incl. the new Content Set) check the URL format only const editURL = '/exact/contentset_edit_modal.php?id='; const expected = myContentSetsByName.map((s, i) => ({ position: i + 1, name: s.name, count: s.totalItems, ...(s.id ? { href: `${editURL}${s.id}` } : { hrefContains: editURL }), })); //Item count if (rows.length === expected.length) { console.log(`Passed: Sort by Name item count matched as expected (${expected.length})`); } else { errors.push(`Expected: ${expected.length} items sorted by Name; Actual: ${rows.length}`); } for (const exp of expected) { const actual = rows[exp.position - 1]; if (!actual) { errors.push(`Position ${exp.position}: row not found`); continue; } const nameMatch = actual.name === exp.name; const countMatch = actual.count === exp.count; //Use includes() for href if exp.hrefContains is defined, otherwise exact match const hrefMatch = exp.hrefContains ? actual.href.includes(exp.hrefContains) : actual.href === exp.href; if (nameMatch && countMatch && hrefMatch) { console.log(`Passed: Sort Order ${exp.position} - ${exp.name} | Count: ${exp.count} | href | All matched as expected`); } else { if (!nameMatch) errors.push(`Sort Order ${exp.position} name - Expected: "${exp.name}"; Actual: "${actual.name}"`); if (!countMatch) errors.push(`Sort Order ${exp.position} count - Expected: "${exp.count}"; Actual: "${actual.count}"`); if (!hrefMatch) errors.push(`Sort Order ${exp.position} href - Expected: "${exp.href || exp.hrefContains + ''}"; Actual: "${actual.href}"`); } } //Report any unexpected extra items rows.slice(expected.length).forEach(extra => { errors.push(`Unexpected item at Sort Order ${extra.position}: "${extra.name}" (${extra.count})`); }); //Throw summary if any validation failed if (errors.length > 0) { if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Click newly created Content Set edit icon', action: async () => { await driver.findElement(By.css('#mycontentlist > #content-org-list > #content-org-list > .content-org .fa')).click(); console.log(`----->| NEXT ACTION: Clicked ${createContentSetName} edit icon`); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.modal-lg .modal-title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Modal did not become visible within 20 seconds'); console.log('Passed: DIV loaded successfully'); } }, //Edit DIV - title { description: 'Edit DIV title', action: async () => { const multipleChecks = 'Edit DIV title'; const element = await driver.findElement(By.css('.modal-lg .modal-title')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Edit Content Set') { console.log('Passed: Edit Content Set DIV title matched as expected'); } else { errors.push(`Expected: DIV title: "Edit Content Set"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Edit Content Set DIV title font matched as expected'); } else { errors.push(`Expected: Edit Content Set DIV title font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '18px') { console.log('Passed: Edit Content Set DIV title font-size matched as expected'); } else { errors.push(`Expected: Edit Content Set DIV title font-size "18px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Edit Content Set DIV title color matched as expected'); } else { errors.push(`Expected: Edit Content Set DIV title color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Edit DIV - label { description: 'Edit DIV - Content Set Name label', action: async () => { const multipleChecks = 'Edit DIV label'; const element = await driver.findElement(By.css('#divname > .form-label')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === 'Content Set Name') { console.log('Passed: Content Set Name DIV label matched as expected'); } else { errors.push(`Expected: "Content Set Name"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Content Set Name label font matched as expected'); } else { errors.push(`Expected: Content Set Name label font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '15px') { console.log('Passed: Content Set Name label font-size matched as expected'); } else { errors.push(`Expected: Content Set Name label font-size "15px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Content Set Name label color matched as expected'); } else { errors.push(`Expected: Content Set Name label color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Edit DIV - textbox { description: 'Edit DIV textbox', action: async () => { const multipleChecks = 'Edit DIV texbox'; const element = await driver.findElement(By.css('#orgname')); //Get text, font, size, and color const elementText = await element.getAttribute('value'); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); //Track all errors const errors = []; //text if (elementText === `${createContentSetName}`) { console.log(`Passed: ${createContentSetName} textbox text matched as expected`); } else { errors.push(`Expected: "${createContentSetName}"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log(`Passed: ${createContentSetName} textbox font matched as expected`); } else { errors.push(`Expected: ${createContentSetName} textbox font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '14px') { console.log(`Passed: ${createContentSetName} textbox font-size matched as expected`); } else { errors.push(`Expected: ${createContentSetName} textbox font-size "14px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log(`Passed: ${createContentSetName} textbox color matched as expected`); } else { errors.push(`Expected: ${createContentSetName} textbox color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Delete button { description: 'Delete button', action: async () => { const multipleChecks = 'Delete button'; const element = await driver.findElement(By.css('#orgdelete')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementButtonColor = await element.getCssValue('background-color'); const elementTooltip = await element.getAttribute('title'); //Track all errors const errors = []; //text if (elementText === 'Delete') { console.log('Passed: Delete button text matched as expected'); } else { errors.push(`Expected: "Delete"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Delete button text font matched as expected'); } else { errors.push(`Expected: Delete button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Delete button text font-size matched as expected'); } else { errors.push(`Expected: Delete button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Delete button text color matched as expected'); } else { errors.push(`Expected: Delete button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //button color if (elementButtonColor === 'rgba(220, 53, 69, 1)') { console.log('Passed: Delete button background color matched as expected'); } else { errors.push(`Expected: Delete button background color: "rgba(220, 53, 69, 1)"; Actual: "${elementButtonColor}"`); } //tooltip if (elementTooltip === '') { console.log('Passed: Delete button tool-tip disabled as expected'); } else { errors.push(`Expected: Delete button tool-tip: "no tool-tip"; Actual: "${elementTooltip}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Save button { description: 'Save button', action: async () => { const multipleChecks = 'Save button'; const element = await driver.findElement(By.css('#modalop')); //Get text, font, color, and size const elementText = await element.getAttribute('value'); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Save') { console.log('Passed: Save button text matched as expected'); } else { errors.push(`Expected: "Save"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Save button text font matched as expected'); } else { errors.push(`Expected: Save button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Save button text font-size matched as expected'); } else { errors.push(`Expected: Save button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Save button text color matched as expected'); } else { errors.push(`Expected: Save button text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(48, 200, 172, 1)') { console.log('Passed: Save button background color matched as expected'); } else { errors.push(`Expected: Save button background color: "rgba(48, 200, 172, 1)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //Close button { description: 'Close button', action: async () => { const multipleChecks = 'Close button'; const element = await driver.findElement(By.css('#modalclose')); //Get text, font, color, and size const elementText = await element.getText(''); const elementFont = await element.getCssValue('font-family'); const elementSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementColor = await element.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'Close') { console.log('Passed: Close button text matched as expected'); } else { errors.push(`Expected: "Close"; Actual: "${elementText}"`); } //font-family if (elementFont === "Poppins, sans-serif") { console.log('Passed: Close button text font matched as expected'); } else { errors.push(`Expected: Close button text font "Poppins, sans-serif"; Actual: ${elementFont}`); } //font size if (elementSize === '16px') { console.log('Passed: Close button text font-size matched as expected'); } else { errors.push(`Expected: Close button text font-size "16px"; Actual: "${elementSize}"`); } //text color if (elementTextColor === 'rgba(40, 39, 90, 1)') { console.log('Passed: Close button text color matched as expected'); } else { errors.push(`Expected: Close button text color: "rgba(40, 39, 90, 1)"; Actual: "${elementTextColor}"`); } //color if (elementColor === 'rgba(0, 0, 0, 0)') { console.log('Passed: Close button background color matched as expected'); } else { errors.push(`Expected: Close button background color: "rgba(0, 0, 0, 0)"; Actual: "${elementColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION { description: 'Delete created content and validate alert message', action: async () => { const multipleChecks = 'Delete content alert'; const errors = []; //Wait for modal to be ready await driver.wait(until.elementLocated(By.css('#myModal')), 5000); await driver.sleep(1000); //Click delete button await driver.wait(until.elementLocated(By.css('#orgdelete')), 5000).click(); await driver.sleep(2500); console.log('----->| NEXT ACTION: Clicked Delete button'); //Wait for confirm dialog to appear const alert = await driver.wait(until.alertIsPresent(), 5000); console.log('----->| NEXT ACTION: Alert dialog appeared'); //Get alert text const alertText = await alert.getText(); //Validate alert message text const expectedText = `Are you sure you want to delete ${createContentSetName}? This operation cannot be undone or recovered.`; if (alertText === expectedText) { console.log('Passed: Delete alert message text matched as expected'); } else { errors.push(`Expected: "${expectedText}"; Actual: "${alertText}"`); } //Throw summary before dismissing if errors found if (errors.length > 0) { await alert.dismiss(); //Cancel the delete if validation failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } throw new Error(errors.join('\n')); } //Accept the alert (click OK) to proceed with deletion await alert.accept(); console.log('----->| NEXT ACTION: Clicked OK to confirm alert DIV Content Set deletion'); await driver.sleep(2000); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('#statalert')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'Alert banner did not become visible within 20 seconds'); } }, //Alert banner { description: 'Alert banner', action: async () => { const multipleChecks = 'Alert banner'; const element = await driver.findElement(By.css('strong')); const elementBanner = await driver.findElement(By.css('#statalert')); //Get text, font, size, and color const elementText = await element.getText(''); const elementTextFont = await element.getCssValue('font-family'); const elementTextSize = await element.getCssValue('font-size'); const elementTextColor = await element.getCssValue('color'); const elementBannerColor = await elementBanner.getCssValue('background-color'); //Track all errors const errors = []; //text if (elementText === 'The content set was successfuly deleted') { console.log('Passed: Confirmation text matched as expected'); } else { errors.push(`Expected: Confirmation: "The content set was successfuly deleted"; Actual: "${elementText}"`); } //font-family if (elementTextFont === "Poppins, sans-serif") { console.log('Passed: Confirmation text font matched as expected'); } else { errors.push(`Expected: Confirmation text font "Poppins, sans-serif"; Actual: ${elementTextFont}`); } //font size if (elementTextSize === '13px') { console.log('Passed: Confirmation text font-size matched as expected'); } else { errors.push(`Expected: Confirmation text font-size "13px"; Actual: "${elementTextSize}"`); } //color if (elementTextColor === 'rgba(255, 255, 255, 1)') { console.log('Passed: Confirmation text color matched as expected'); } else { errors.push(`Expected: Confirmation text color: "rgba(255, 255, 255, 1)"; Actual: "${elementTextColor}"`); } //banner color if (elementBannerColor === 'rgba(40, 167, 69, 1)') { console.log('Passed: Confirmation banner color matched as expected'); } else { errors.push(`Expected: Confirmation banner color: "rgba(40, 167, 69, 1)"; Actual: "${elementBannerColor}"`); } //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } //Wait until the element exists in the DOM const correlationCoveragesImage = await driver.wait( until.elementLocated(By.css('.gap-idle-wrap > img')), 20000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(correlationCoveragesImage), 20000 ); //Wait for page to load completely await driver.wait(async () => { const readyState = await driver.executeScript('return document.readyState'); return readyState === 'complete'; }, 20000, 'ExACT Dashboard did not fully load within 30 seconds'); console.log('Passed: ExACT Dashboard loaded successfully'); } }, //Verify content was deleted { description: 'Verify Content Set deletion', action: async () => { //Clear filter and search for the deleted Content Set by name const filterInput = await driver.wait(until.elementLocated(By.css('#content-org-filter')), 5000); await filterInput.clear(); //Trigger keyup after clear to reset the list await driver.executeScript(`$(document.querySelector('#content-org-filter')).trigger('keyup');`); await driver.sleep(500); //Type the deleted Content Set name to filter for it specifically for (const char of 'Automated Algebra') { await filterInput.sendKeys(char); await driver.executeScript(`$(document.querySelector('#content-org-filter')).trigger('keyup');`); await driver.sleep(100); } console.log('----->| NEXT ACTION: Filtered list by "Automated Algebra"'); await driver.sleep(1000); //Check visible items only const visibleNames = await driver.executeScript(` const items = document.querySelectorAll('#mycontentlist .orgname'); return Array.from(items) .filter(el => { const style = window.getComputedStyle(el.closest('li')); return style.display !== 'none' && style.visibility !== 'hidden'; }) .map(el => el.querySelector('a:first-child').textContent.trim()); `); //console.log('----->| DEBUG visible items after filter:', JSON.stringify(visibleNames)); //Verify deleted Content Set is NOT in the visible list if (visibleNames.includes(createContentSetName)) { throw new Error(`Expected: "${createContentSetName}" to be deleted; Actual: Still visible in list`); } else { console.log(`Passed: ${createContentSetName} successfully deleted and no longer visible`); } //Clear the filter - .clear() fires no key events, so trigger keyup (same event the filter listens to) await filterInput.clear(); await driver.executeScript(`$(document.querySelector('#content-org-filter')).trigger('keyup');`); console.log('----->| NEXT ACTION: Deleted filter term'); //Wait until the full list is visible again (all original Content Sets; new one was deleted) await driver.wait(async () => { const visibleCount = await driver.executeScript(` return Array.from(document.querySelectorAll('#mycontentlist .orgname')).filter(el => { const style = window.getComputedStyle(el.closest('li')); return style.display !== 'none' && style.visibility !== 'hidden'; }).length; `); return visibleCount === myContentSets.length; }, 10000, `My Content list did not reset to ${myContentSets.length} items within 10 seconds after clearing filter`, 300); console.log('Passed: My Content list reset after clearing filter'); } }, //NEXT ACTION - reopen My Content Totals DIV (table rows only load when the DIV is opened) { description: 'Click My Content Items Count button (re-validate totals after delete)', action: async () => { await driver.findElement(By.css('#content_total')).click(); console.log('----->| NEXT ACTION: Clicked Total items count button'); //Wait until the modal title is located AND visible await driver.wait(async () => { try { const element = await driver.findElement(By.css('.content_modal_title')); return await element.isDisplayed(); } catch (e) { return false; //Element not ready yet, keep waiting } }, 20000, 'My Content Totals DIV did not become visible within 20 seconds'); //Wait until table rows have loaded await driver.wait(until.elementLocated(By.css('#content_tbody > tr')), 20000, 'My Content Totals table rows did not load within 20 seconds'); console.log('Passed: My Content Totals DIV loaded successfully'); } }, //My Content Totals DIV - Table data (Content Set, Total Items, Unique Titles, Unique IDs) { description: 'My Content Totals DIV - Table Content Set, Total Items, Unique Titles, and Unique IDs', action: async () => { const multipleChecks = 'My Content Totals table data'; //Expected rows in display order: [Content Set, Total Items, Unique Titles, Unique IDs] (from myContentSets constant) const expectedRows = myContentSets.map(s => [s.name, s.totalItems, s.uniqueTitles, s.uniqueIds]); const columns = ['Content Set', 'Total Items', 'Unique Titles', 'Unique IDs']; //Expected cell styles per column [Content Set, Total Items, Unique Titles, Unique IDs] const expectedStyles = [ { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, { font: 'Poppins, sans-serif', size: '12px', color: 'rgba(40, 39, 90, 1)' }, ]; //Wait for table rows to render await driver.wait(until.elementLocated(By.css('#content_tbody > tr')), 20000, 'My Content Totals table rows did not load within 20 seconds'); //Read all cells in one call (textContent also works for rows scrolled out of view) //Colors are normalized from rgb() to rgba() to match Selenium's getCssValue() format const cellData = await driver.executeScript(` const toRgba = c => c.startsWith('rgb(') ? c.replace('rgb(', 'rgba(').replace(')', ', 1)') : c; return Array.from(document.querySelectorAll('#content_tbody > tr')).map(tr => Array.from(tr.querySelectorAll('td')).map(td => { const cs = window.getComputedStyle(td); return { text: td.textContent.trim(), font: cs.fontFamily, size: cs.fontSize, color: toRgba(cs.color) }; }) ); `); const actualRows = cellData.map(row => row.map(cell => cell.text)); //Track all errors const errors = []; //Row count if (actualRows.length === expectedRows.length) { console.log(`Passed: My Content Totals table row count matched as expected, ${expectedRows.length}`); } else { errors.push(`Expected: ${expectedRows.length} table rows; Actual: ${actualRows.length}`); } //Cell-by-cell comparison expectedRows.forEach((expected, rowIndex) => { const actual = actualRows[rowIndex]; if (!actual) { errors.push(`Row ${rowIndex + 1} ("${expected[0]}") missing from table`); return; } const rowErrors = []; columns.forEach((column, colIndex) => { if (actual[colIndex] !== expected[colIndex]) { rowErrors.push(`${column} Expected: "${expected[colIndex]}"; Actual: "${actual[colIndex]}"`); } //Style checks (font-family, font-size, color) const cell = cellData[rowIndex][colIndex]; const style = expectedStyles[colIndex]; if (!cell) return; if (cell.font !== style.font) { rowErrors.push(`${column} font Expected: "${style.font}"; Actual: "${cell.font}"`); } if (cell.size !== style.size) { rowErrors.push(`${column} font-size Expected: "${style.size}"; Actual: "${cell.size}"`); } if (cell.color !== style.color) { rowErrors.push(`${column} color Expected: "${style.color}"; Actual: "${cell.color}"`); } }); if (rowErrors.length === 0) { console.log(`Passed: Row ${rowIndex + 1} "${expected[0]}" - ${columns.map((c, i) => `${c}: ${expected[i]}`).join(', ')} | Text font, size, and color all matched as expected`); } else { errors.push(`Row ${rowIndex + 1} ("${expected[0]}"): ${rowErrors.join('; ')}`); } }); //Report any unexpected extra rows actualRows.slice(expectedRows.length).forEach((extra, i) => { errors.push(`Unexpected row ${expectedRows.length + i + 1}: ${JSON.stringify(extra)}`); }); //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, //NEXT ACTION - close My Content Totals DIV before logging out { description: 'Close My Content Totals DIV', action: async () => { const closeButton = await driver.findElement(By.css('#all_content_modal .btn-secondary')); await closeButton.click(); console.log('----->| NEXT ACTION: Clicked My Content Totals DIV Close button'); //Wait until the DIV is hidden await driver.wait(async () => { try { return !(await driver.findElement(By.css('.content_modal_title')).isDisplayed()); } catch (e) { return true; } }, 10000, 'My Content Totals DIV did not close within 10 seconds'); console.log('Passed: My Content Totals DIV closed'); } }, //NEXT ACTION { description: 'Log Out', action: async () => { const multipleChecks = 'Log Out'; //Track all errors const errors = []; await driver.findElement(By.css('#navbarDropdown > span')).click(); await driver.sleep(2000); await driver.findElement(By.linkText('Log Out')).click(); console.log('----->| NEXT ACTION: Clicked User Profile and Logged Out'); //Wait until the element exists in the DOM const exactLogo = await driver.wait( until.elementLocated(By.css('.logo')), 30000 ); //Wait until it becomes visible await driver.wait( until.elementIsVisible(exactLogo), 30000 ); //Wait for page to load completely await driver.wait(async () => { const readyState = await driver.executeScript('return document.readyState'); return readyState === 'complete'; }, 30000, 'ExACT Langing page did not fully load within 30 seconds'); console.log('Passed: ExACT Landing Page loaded successfully'); //Throw summary if any validation failed if (errors.length > 0) { //Add summary line only when multiple checks failed if (errors.length > 1) { errors.unshift(`${multipleChecks} - Multiple validation checks did not pass`); } //Throw collected messages — catch() will prefix with "FAILED:" throw new Error(errors.join('\n')); } } }, ]; const logStep = (worksheet, stepDescription, status, stepIndex, timestamp, screenshotPath) => { const json_data = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); json_data.push([stepIndex, stepDescription, status, timestamp, screenshotPath]); return XLSX.utils.aoa_to_sheet(json_data); }; const takeScreenshot = async (driver, stepIndex, stepDescription) => { const screenshot = await driver.takeScreenshot(); const dir = path.join('screenshots', 'myContentWidget'); const screenshotFileName = `step_${stepIndex}_${stepDescription.replace(/\s+/g, '_')}.png`; const screenshotPath = path.join(dir, screenshotFileName); fs.writeFileSync(screenshotPath, screenshot, 'base64'); return screenshotPath; }; const resultsFile = path.join('results', 'myContentWidget.xlsx'); const clearOldResults = () => { //Remove old screenshots const screenshotDir = path.join('screenshots', 'myContentWidget'); if (fs.existsSync(screenshotDir)) { fs.rmSync(screenshotDir, { recursive: true, force: true }); } fs.mkdirSync(screenshotDir, { recursive: true }); //Remove old Excel file if (fs.existsSync(resultsFile)) { fs.unlinkSync(resultsFile); } }; try { //Initialize workbook and worksheet clearOldResults(); //Initialize workbook and worksheet workbook = XLSX.utils.book_new(); worksheet = XLSX.utils.aoa_to_sheet([ ['Step Number', 'Step Description', 'Status', 'Timestamp', 'Screenshot'] ]); XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); //Log test start time worksheet = logStep(worksheet, 'Test Start', 'Started', '', startTime, ''); let allStepsPassed = true; const failedSteps = []; //Track failed steps for (const step of steps) { const stepIndex = steps.indexOf(step) + 1; const timestamp = new Date().toISOString(); try { await step.action(); worksheet = logStep( worksheet, step.description, 'Passed', stepIndex, timestamp, null ); } catch (error) { const screenshotPath = await takeScreenshot( driver, stepIndex, `${step.description}_Failed` ); worksheet = logStep( worksheet, step.description, 'Failed', stepIndex, timestamp, screenshotPath ); console.error(`Step ${stepIndex} FAILED: ${error.message}`); failedSteps.push(`Step ${stepIndex}: ${step.description} - ${error.message}`); //Track failed step testStatus = 'Failed'; allStepsPassed = false; } } if (allStepsPassed) { console.log('------------->| THE END |<-------------'); console.log('PASSED: DASHBOARD - My Content Widget(myContentWidget); All test steps completed successfully'); testStatus = 'Passed'; } else { console.log('------------->| THE END |<-------------'); console.log('FAILED: DASHBOARD - My Content Widget(myContentWidget); One or more test steps failed'); console.log('FAILED STEPS:'); failedSteps.forEach(step => console.error(` ✗ ${step}`)); testStatus = 'Failed'; } //Log test end time const endTime = new Date().toISOString(); worksheet = logStep(worksheet, 'Test End', testStatus, '', endTime, ''); //Write the updated worksheet back to the workbook and save it workbook.Sheets['Sheet1'] = worksheet; XLSX.writeFile(workbook, resultsFile); } finally { await driver.quit(); } })(); } export default myContentWidget;