diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java index a4007a0..e411642 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java @@ -8,6 +8,7 @@ import ntnu.systemutvikling.team6.models.Donation; import ntnu.systemutvikling.team6.models.DonationRegistry; import ntnu.systemutvikling.team6.scraper.APICharityData; +import ntnu.systemutvikling.team6.scraper.URLCharityScraper; /** * Manages the Database with MySQL tables and JDBC. @@ -74,6 +75,10 @@ charity_name VARCHAR(255) NOT NULL, charity_link VARCHAR(255) NOT NULL, pre_approved TINYINT NOT NULL, status VARCHAR(255) NOT NULL, + description TEXT, + logoURL TEXT, + categories TEXT, + key_values TEXT, UNIQUE KEY unique_org_number (org_number) ) ENGINE=InnoDB; @@ -127,13 +132,17 @@ public void addAPIDataToTable(List charities) { conn.setAutoCommit(false); String sql_query = """ - INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, pre_approved, status) - VALUES (?, ?, ?, ?, ?, ?) + INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, pre_approved, status, description, logoURL, categories, key_values) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE charity_name = VALUES(charity_name), charity_link = VALUES(charity_link), pre_approved = VALUES(pre_approved), - status = VALUES(status) + status = VALUES(status), + description = VALUES(description), + logoURL = VALUES(logoURL), + categories = VALUES(categories), + key_values = VALUES(key_values) """; try (PreparedStatement ps = conn.prepareStatement(sql_query)) { @@ -143,12 +152,24 @@ INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, p } else { ps.setString(1, charity.getUUID().toString()); } + // Scrapes description, logo, categories, and key values from IK + URLCharityScraper urlScraper = new URLCharityScraper(charity.getURL()); + urlScraper.scrapeCharityPage(); + + charity.setDescription(urlScraper.getDescription()); + charity.setCategory(urlScraper.getCategories()); + charity.setLogoURL(urlScraper.getLogoURL()); + charity.setKeyValues(urlScraper.getKeyValues()); ps.setString(2, charity.getOrg_number().replaceAll("\\s", "")); ps.setString(3, charity.getName()); - ps.setString(4, charity.getDescription()); - ps.setBoolean(5, charity.getPreApproved()); // Description is the link + ps.setString(4, charity.getURL()); + ps.setBoolean(5, charity.getPreApproved()); ps.setString(6, charity.getStatus()); + ps.setString(7, charity.getDescription()); + ps.setString(8, charity.getLogoURL()); + ps.setString(9, charity.getCategory()); + ps.setString(10, charity.getKeyValues()); ps.addBatch(); } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java index d97d996..4643d87 100644 --- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java @@ -20,8 +20,8 @@ public class Charity { /* Name of the charity */ private String name; - /* Description of the charity's mission and activities */ - private String description; + /* URL of the charity */ + private String url; /* Is the charity verified? */ private String status; @@ -31,6 +31,15 @@ public class Charity { /* Category for the charity */ private String category; + /* Description for the charity */ + private String description; + + /* URL for the logo of the charity */ + private String logoURL; + + /* Key values for the charity */ + private String keyValues; + /* List that contains the charity's Feedbacks */ private List feedbacks; @@ -48,7 +57,7 @@ public Charity( this.UUID = java.util.UUID.randomUUID(); this.org_number = org_number.replaceAll("\\s", ""); this.name = name; - this.description = "Les mer her: " + link; + this.url = link; this.is_pre_approved = is_pre_approved; this.status = status; this.feedbacks = new ArrayList<>(); @@ -74,11 +83,14 @@ public Charity( this.UUID = UUID.fromString(uuid); this.org_number = org_number.replaceAll("\\s", ""); this.name = name; - this.description = link; + this.url = link; this.is_pre_approved = is_pre_approved; this.status = status; - this.feedbacks = new ArrayList<>(); this.category = ""; + this.description = ""; + this.logoURL = ""; + this.keyValues = ""; + this.feedbacks = new ArrayList<>(); } /** Getters for the charity's attributes. */ @@ -110,8 +122,20 @@ public String getName() { return name; } + public String getURL() { + return this.url; + } + public String getDescription() { - return description; + return this.description; + } + + public String getLogoURL() { + return this.logoURL; + } + + public String getKeyValues() { + return this.keyValues; } /** Setter for verification status. This one sets the charity as verified. */ @@ -123,4 +147,24 @@ public void setVerified() { public void setUnverified() { this.status = "Veto"; } + + /** Setter for categories. */ + public void setCategory(String category) { + this.category = category; + } + + /** Setter for description. */ + public void setDescription(String description) { + this.description = description; + } + + /** Setter for the URL of the charity's logo. */ + public void setLogoURL(String url) { + this.logoURL = url; + } + + /** Setter for the charity's key values. */ + public void setKeyValues(String values) { + this.keyValues = values; + } } diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/URLCharityScraper.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/URLCharityScraper.java new file mode 100644 index 0000000..ce563c9 --- /dev/null +++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/URLCharityScraper.java @@ -0,0 +1,278 @@ +package ntnu.systemutvikling.team6.scraper; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +/** + * Class for scraping the description, URL of the logo, string of categories, and key values of the charities + * registered in IK. + */ +public class URLCharityScraper { + private final String url; + private final WebDriver driver; + private String description; + private String logoURL; + private final List categories; + private final List keyValues; + + /** + * Constructor used for production code. + * + *

It initializes the lists used for categories and keyValues, as well as defining the parameters used + * for the selenium Chromium-based browser that does the scraping.

+ * + * @param url the URL for the charity's webpage on IK + */ + public URLCharityScraper(String url) { + this.categories = new ArrayList<>(); + this.keyValues = new ArrayList<>(); + + ChromeOptions options = new ChromeOptions(); + options.addArguments("--headless=new"); + options.addArguments("--window-size=1920,1080"); + options.addArguments("--disable-gpu"); + options.addArguments("--no-sandbox"); + options.addArguments("--disable-dev-shm-usage"); + + this.url = url; + this.driver = new ChromeDriver(options); + } + + /** + * Constructor used for testing. + * + *

It accepts both a url (should ideally be a dud) and a {@link WebDriver} as parameters. The WebDriver is + * passed to make testing easier.

+ * + * @param url the URL for the charity's webpage on IK (for this constructor it should not be a real URL) + * @param driver the {@code WebDriver} object used for scraping + */ + public URLCharityScraper(String url, WebDriver driver) { + this.categories = new ArrayList<>(); + this.keyValues = new ArrayList<>(); + this.url = url; + this.driver = driver; + } + + /** + * Creates a {@link WebDriverWait} object for halting scraping until the correct pre-conditions are met. + * + * @return the {@code WebDriverWait} object to be used in the methods + */ + protected WebDriverWait createWait() { + return new WebDriverWait(driver, Duration.ofSeconds(30)); + } + + /** + * Calls the {@code findElements} method from the {@code WebDriver} object and returns a list of the returned + * {@link WebElement} objects. + * + * @param by a selector for {@code WebElement} objects + * @return a list of found {@code WebElement} objects matching the given selector + */ + protected List findElements(By by) { + return driver.findElements(by); + } + + /** + * Calls the {@code findElement} method from the {@code WebDriver} object and returns a list of the returned + * {@code WebElement} objects. + * + * @param by a selector for {@code WebElement} objects + * @return a list of found {@code WebElement} objects matching the given selector + */ + protected WebElement findElement(By by) { + return driver.findElement(by); + } + + /** + * Quits the driver instance, making it unusable. + */ + protected void closeDriver() { + driver.quit(); + } + + /** + * Scrapes the URL for the paragraphs containing the description of the charity. + */ + protected void updateDescription() { + WebDriverWait wait = createWait(); + StringBuilder descriptionString = new StringBuilder(); + + wait.until(ExpectedConditions.numberOfElementsToBeMoreThan( + By.cssSelector(".information div"), 0)); + + List firstDescription = + findElements(By.cssSelector(".information div p")); + + for (WebElement element : firstDescription) { + if (!element.getText().isBlank()) { + descriptionString.append(element.getText()).append("\n\n"); + } + } + + List readMoreLinks = + findElements(By.cssSelector("a.read-more")); + + if (!readMoreLinks.isEmpty()) { + WebElement readMore = findElement(By.cssSelector("a.read-more")); + readMore.click(); + + wait.until(ExpectedConditions.visibilityOfElementLocated( + By.cssSelector(".extra-info"))); + } + + List extraDescription = + findElements(By.cssSelector(".extra-info p")); + + for (WebElement element : extraDescription) { + if (!element.getText().isBlank()) { + descriptionString.append(element.getText()).append("\n\n"); + } + } + + this.description = descriptionString.toString(); + } + + /** + * Scrapes the URL for the image URL of the logo for the charity. + */ + void updateLogo() { + WebDriverWait wait = createWait(); + + wait.until(ExpectedConditions.visibilityOfElementLocated( + By.cssSelector(".logo > img"))); + + WebElement logo = findElement(By.cssSelector(".logo > img")); + this.logoURL = logo.getAttribute("src"); + } + + /** + * Scrapes the URL for the category labels containing the categories for the charity. + */ + void updateCategories() { + WebDriverWait wait = createWait(); + + wait.until(ExpectedConditions.visibilityOfElementLocated( + By.cssSelector(".tag-label"))); + + List elements = + findElements(By.cssSelector(".tag-label")); + + for (WebElement element : elements) { + this.categories.add(element.getText()); + } + } + + /** + * Scrapes the URL for the statistics of the charity; the percentage collected, the percentage that goes to the + * administration, and the percentage that is put towards the cause. + */ + void updateKeyValues() { + WebDriverWait wait = createWait(); + + String percentage; + WebElement element; + + wait.until(ExpectedConditions.visibilityOfElementLocated( + By.xpath("//li[.//h2[normalize-space()='Innsamlingsprosent']]//div[@class='graph']"))); + + element = findElement(By.xpath( + "//li[.//h2[normalize-space()='Innsamlingsprosent']]//div[@class='graph']")); + percentage = element.getAttribute("data-percentage"); + this.keyValues.add(percentage); + + wait.until(ExpectedConditions.visibilityOfElementLocated( + By.xpath("//li[.//h2[normalize-space()='Administrasjonsprosent']]//div[@class='graph']"))); + + element = findElement(By.xpath( + "//li[.//h2[normalize-space()='Administrasjonsprosent']]//div[@class='graph']")); + percentage = element.getAttribute("data-percentage"); + this.keyValues.add(percentage); + + wait.until(ExpectedConditions.visibilityOfElementLocated( + By.xpath("//li[.//h2[normalize-space()='Formålsprosent']]//div[@class='graph']"))); + + element = findElement(By.xpath( + "//li[.//h2[normalize-space()='Formålsprosent']]//div[@class='graph']")); + percentage = element.getAttribute("data-percentage"); + this.keyValues.add(percentage); + } + + /** + * Runs all the scraper methods at once, updating the object parameters. + */ + public void scrapeCharityPage() { + try { + driver.get(this.url); + + updateDescription(); + updateLogo(); + updateCategories(); + updateKeyValues(); + + } finally { + closeDriver(); + } + } + + /** + * Returns the description of the charity. + * + * @return a String containing the description of the charity. + */ + public String getDescription() { + return description; + } + + /** + * Returns the URL of the logo for the charity. + * + * @return a String containing the URL for the logo of the charity. + */ + public String getLogoURL() { + return logoURL; + } + + /** + * Returns a String of the categories for the charity with ',' as a delimiter. + * + * @return a String of strings containing the categories for the charity + */ + public String getCategories() { + StringBuilder categoriesString = new StringBuilder(); + + for (int i = 0; i < this.categories.size(); i++) { + categoriesString.append(this.categories.get(i)); + if (i < this.categories.size() - 1) { + categoriesString.append(","); + } + } + return categoriesString.toString(); + } + + /** + * Returns a String of the key value percentages for the charity with ':' as a delimiter, verified by IK. + * + * @return a String of the key values for the charity- + */ + public String getKeyValues() { + StringBuilder keyValuesString = new StringBuilder(); + + for (int i = 0; i < this.keyValues.size(); i++) { + keyValuesString.append(this.keyValues.get(i)); + if (i < this.keyValues.size() - 1) { + keyValuesString.append(":"); + } + } + return keyValuesString.toString(); + } +} \ No newline at end of file diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java index d3522ed..c90a297 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/DAO/DonationDAOTest.java @@ -20,7 +20,13 @@ void setUp() { DatabaseManager manager = new DatabaseManager(); manager.createTables(); - charity = new Charity("123456", "https://test.org", "Test Charity", true, "approved"); + charity = + new Charity( + "123456", + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/", + "Test Charity", + true, + "approved"); manager.addAPIDataToTable(java.util.List.of(charity)); } diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java index 086ed69..78be78a 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DatabaseManagerTest.java @@ -18,6 +18,21 @@ public void setUp() throws SQLException { this.dbManager = new DatabaseManager(); } + @Test + public void test() { + dbManager.createTables(); + + String org_number = "12345"; + String name = "Test Charity"; + String status = "approved"; + String url = + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; + boolean is_pre_approved = false; + + Charity charity = new Charity(org_number, url, name, is_pre_approved, status); + dbManager.addAPIDataToTable(List.of(charity)); + } + // Make sure you're connected to the NTNU network for this to work @Test public void testConnectionShouldReturnTrue() { @@ -42,7 +57,8 @@ void updateCharitiesShouldInsertCorrectData() throws SQLException { String org_number = "12345"; String name = "Test Charity"; String status = "approved"; - String url = "https://www.svindel.no"; + String url = + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; boolean is_pre_approved = false; Charity charity = new Charity(org_number, url, name, is_pre_approved, status); @@ -63,7 +79,8 @@ void updateCharitiesShouldRemoveDataNotInList() throws SQLException { String org_number = "12345"; String name = "Svindelorg"; String status = "approved"; - String url = "https://www.svindel.no"; + String url = + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; boolean is_pre_approved = false; var charity1 = new Charity(org_number, url, name, is_pre_approved, status); @@ -71,7 +88,8 @@ void updateCharitiesShouldRemoveDataNotInList() throws SQLException { org_number = "23456"; name = "SvindelKoin"; status = "approved"; - url = "https://www.svindel.net"; + url = + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; is_pre_approved = true; var charity2 = new Charity(org_number, url, name, is_pre_approved, status); @@ -79,7 +97,8 @@ void updateCharitiesShouldRemoveDataNotInList() throws SQLException { org_number = "345672"; name = "Arme Svindlere"; status = "approved"; - url = "https://www.armesvindlere.com"; + url = + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/"; is_pre_approved = false; var charity3 = new Charity(org_number, url, name, is_pre_approved, status); @@ -113,7 +132,13 @@ void updateCharitiesShouldRemoveDataNotInList() throws SQLException { @Test void tempTableShouldNotExistAfterUpdating() throws SQLException { - Charity charity = new Charity("99999", "https://temp.no", "Temp Charity", false, "approved"); + Charity charity = + new Charity( + "99999", + "https://www.innsamlingskontrollen.no/organisasjoner/adra-norge-adventist-development-and-relief-agency-norway/", + "Temp Charity", + false, + "approved"); dbManager.addAPIDataToTable(List.of(charity)); diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java index e382fe5..8bcc674 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/models/CharityTest.java @@ -37,7 +37,7 @@ public void testGettingNameShouldWork() { @Test public void testGettingDescriptionShouldWork() { - assertEquals("Les mer her: www.aaaa.com", charity.getDescription()); + assertEquals("www.aaaa.com", charity.getURL()); } /** Getter and setter for IsVerified should be able to switch between true and false */ diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java index 40c3ce4..1ab7413 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/APICharityScraperTest.java @@ -86,8 +86,8 @@ void parsedJSONShouldHaveCorrectValues() throws URISyntaxException { assertEquals("Misjonsalliansen", d.getName(), "Name parameter should be correct."); assertEquals("approved", d.getStatus(), "Status parameter should be correct."); assertEquals( - "Les mer her: https://www.innsamlingskontrollen.no/organisasjoner/misjonsalliansen/", - d.getDescription(), + "https://www.innsamlingskontrollen.no/organisasjoner/misjonsalliansen/", + d.getURL(), "Url parameter should be correct."); assertFalse(d.getPreApproved(), "Is_pre_approved parameter should be correct."); } diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/URLCharityScraperTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/URLCharityScraperTest.java new file mode 100644 index 0000000..82c28cb --- /dev/null +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/scraper/URLCharityScraperTest.java @@ -0,0 +1,159 @@ +package ntnu.systemutvikling.team6.scraper; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.WebDriverWait; + +class URLCharityScraperTest { + + private WebDriver driver; + private URLCharityScraper scraper; + + @BeforeEach + void setup() { + driver = mock(WebDriver.class); + + scraper = + new URLCharityScraper("http://test", driver) { + @Override + protected WebDriverWait createWait() { + return mock(WebDriverWait.class); + } + + @Override + protected void closeDriver() {} + }; + } + + @Test + void updateDescriptionShouldReturnCorrectDescriptionWithoutReadMore() { + WebElement p1 = mock(WebElement.class); + WebElement p2 = mock(WebElement.class); + + when(p1.getText()).thenReturn("Short description"); + when(p2.getText()).thenReturn(""); + + when(scraper.findElements(By.cssSelector(".information div p"))).thenReturn(List.of(p1, p2)); + + when(scraper.findElements(By.cssSelector("a.read-more"))).thenReturn(List.of()); + + when(scraper.findElements(By.cssSelector(".extra-info p"))).thenReturn(List.of()); + + scraper.updateDescription(); + + String result = scraper.getDescription(); + + assertTrue( + result.contains("Short description"), "First paragraph should be 'Short description'"); + assertFalse(result.isBlank(), "Second paragraph should be blank."); + } + + @Test + void updateDescriptionShouldReturnCorrectDescriptionWithReadMore() { + WebElement p1 = mock(WebElement.class); + WebElement extra = mock(WebElement.class); + WebElement readMore = mock(WebElement.class); + + when(p1.getText()).thenReturn("Intro"); + when(extra.getText()).thenReturn("Extra info"); + + when(scraper.findElements(By.cssSelector(".information div p"))).thenReturn(List.of(p1)); + + when(scraper.findElements(By.cssSelector("a.read-more"))).thenReturn(List.of(readMore)); + + when(scraper.findElement(By.cssSelector("a.read-more"))).thenReturn(readMore); + + when(scraper.findElements(By.cssSelector(".extra-info p"))).thenReturn(List.of(extra)); + + scraper.updateDescription(); + + String result = scraper.getDescription(); + + verify(readMore).click(); + assertTrue(result.contains("Intro"), "First paragraph should be 'Intro'"); + assertTrue(result.contains("Extra info"), "Second paragraph should be 'Extra info'"); + } + + @Test + void updateLogoShouldReturnCorrectLogoURL() { + WebElement logo = mock(WebElement.class); + + when(scraper.findElement(By.cssSelector(".logo > img"))).thenReturn(logo); + + when(logo.getAttribute("src")).thenReturn("logo.png"); + + scraper.updateLogo(); + + assertEquals("logo.png", scraper.getLogoURL()); + } + + @Test + void updateCategoriesShouldReturnCorrectCategories() { + WebElement c1 = mock(WebElement.class); + WebElement c2 = mock(WebElement.class); + + when(c1.getText()).thenReturn("Health"); + when(c2.getText()).thenReturn("Education"); + + when(scraper.findElements(By.cssSelector(".tag-label"))).thenReturn(List.of(c1, c2)); + + scraper.updateCategories(); + + assertEquals( + "Health,Education", + scraper.getCategories(), + "Should return a string that lists categories with ',' as a delimiter."); + } + + @Test + void updateKeyNumbersShouldReturnCorrectNumbers() { + WebElement e1 = mock(WebElement.class); + WebElement e2 = mock(WebElement.class); + WebElement e3 = mock(WebElement.class); + + when(e1.getAttribute("data-percentage")).thenReturn("80"); + when(e2.getAttribute("data-percentage")).thenReturn("10"); + when(e3.getAttribute("data-percentage")).thenReturn("90"); + + when(scraper.findElement(any(By.class))).thenReturn(e1, e2, e3); + + scraper.updateKeyValues(); + + System.out.println(scraper.getKeyValues()); + + assertEquals( + "80:10:90", + scraper.getKeyValues(), + "Should return a string that lists key values with ':' as a delimiter."); + } + + @Test + void scrapeCharityPageShouldCallAllRelevantMethods() { + URLCharityScraper spyScraper = spy(scraper); + + doNothing().when(spyScraper).updateDescription(); + doNothing().when(spyScraper).updateLogo(); + doNothing().when(spyScraper).updateCategories(); + doNothing().when(spyScraper).updateKeyValues(); + + spyScraper.scrapeCharityPage(); + + // Url should be correct + verify(driver).get("http://test"); + // UpdateDescription should run + verify(spyScraper).updateDescription(); + // UpdateLogo should run + verify(spyScraper).updateLogo(); + // UpdateCategories should run + verify(spyScraper).updateCategories(); + // UpdateKeyValues should run + verify(spyScraper).updateKeyValues(); + } +}