From e24915f40720573bf7b886e401ab1d4469c6266e Mon Sep 17 00:00:00 2001
From: Alexis Baker <61904860+etelan@users.noreply.github.com>
Date: Fri, 26 May 2023 12:49:52 +0100
Subject: [PATCH 1/4] making a pull request
---
src/faq.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/faq.md b/src/faq.md
index 3bc184db..2cb05117 100644
--- a/src/faq.md
+++ b/src/faq.md
@@ -1,30 +1,30 @@
# Frequently Asked Questions
-**Q:** What is this and why should I use it?:
+**Q:** What is this and why should I use it?
**A:** This is a complete redesign of the traditional password generator/storage system. Instead of relying on a hard drive to not to fail, or that backup you keep forgetting to make, Cloverleaf makes a password based on your master password and the application you want a password for. This generated password will be the same every time, no matter what device you're on. You can then copy this and paste it into the app you want to log into.
-**Q:** How do I know you're not looking at my passwords?:
+**Q:** How do I know you're not looking at my passwords?
**A:** When you type, your computer takes the app name and your master password and using them makes a new password. Nothing is ever sent across the internet, other than the code for the website itself. If you really want, you can [download the files](https://github.com/cloverleaf/web/archive/refs/heads/master.zip) for the website, smash your router and run Cloverleaf offline (Not that I'd recommend smashing your router.)
-**Q:** Why did you remake an existing product?:
+**Q:** Why did you remake an existing product?
-**A:** I've openly acknowledged that Cloverleaf is based off the concept of "[Master Password](https://masterpasswordapp.com/)" but why would I spend all this time re-making a working piece of software? The answer is simple: *I don't like how Master Password works.* It has many different offshoots that all look different. It takes 6 variables to make a password and 30 seconds to load (Yes, I timed it.). It makes everything harder than it should be. And that was my motivation to make Cloverleaf. I absolutely love the concept of secure passwords without storing them but hate the current solutions.
+**A:** I've openly acknowledged that Cloverleaf is based off the concept of "[Master Password](https://masterpasswordapp.com/)" but why would I spend all this time re-making a working piece of software? The answer is simple: *I don't like how Master Password works.* It has many different offshoots that all look different. It takes 6 variables to make a password and 30 seconds to load (Yes, I timed it.). It makes everything harder than it should be. And that was my motivation to make Cloverleaf. I absolutely love the concept of secure passwords without storing them but dislike the current solutions.
-**Q:** What if I have multiple accounts on one service?:
+**Q:** What if I have multiple accounts on one service?
**A:** There isn't a system in place for this but there doesn't need to be since you can just type `ACCOUNTNAME PASSWORD` in the password field to get the password for that account.
-**Q:** What are cookies and why do you use them?:
+**Q:** What are cookies and why do you use them?
**A:** A cookie is a small bit of information that a website stores on your computer so that when you come back to the site later, it can remember things from your previous visit. Cloverleaf uses these for remembering what theme you like and a few other things like that.
From a1a9365a98f11c296cfbd72ac04546480c5c45e8 Mon Sep 17 00:00:00 2001
From: Alexis Baker <61904860+etelan@users.noreply.github.com>
Date: Fri, 26 May 2023 12:53:50 +0100
Subject: [PATCH 2/4] style.scss
---
src/style.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/style.scss b/src/style.scss
index 5e6c6446..925c4218 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -1,4 +1,4 @@
-@charset "UTF-8";
+@charset "UTF-8";
// Import only what you need from Materialize
From 63ae075aee97187ce8dc1514c074ad3e1bd9e93d Mon Sep 17 00:00:00 2001
From: Alexis Baker <61904860+etelan@users.noreply.github.com>
Date: Fri, 26 May 2023 13:00:45 +0100
Subject: [PATCH 3/4] Update test_main.py
---
unit_tests/test_main.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unit_tests/test_main.py b/unit_tests/test_main.py
index ce063a64..4d22f255 100644
--- a/unit_tests/test_main.py
+++ b/unit_tests/test_main.py
@@ -1,4 +1,4 @@
-import pytest
+import pytest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.options import Options
From a9ee203a2b9d3f9cd1badba899072b5c9a1e498f Mon Sep 17 00:00:00 2001
From: Alexis Baker <61904860+etelan@users.noreply.github.com>
Date: Fri, 26 May 2023 13:49:27 +0100
Subject: [PATCH 4/4] Update test_main.py
---
unit_tests/test_main.py | 138 ++++++++++++++++++----------------------
1 file changed, 63 insertions(+), 75 deletions(-)
diff --git a/unit_tests/test_main.py b/unit_tests/test_main.py
index 4d22f255..d897a92c 100644
--- a/unit_tests/test_main.py
+++ b/unit_tests/test_main.py
@@ -5,72 +5,41 @@
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.action_chains import ActionChains
from meta import pass_vis
-from meta import get_var
+from meta.get_var import getVar
import json
from urllib.parse import quote
import deep_merge
import warnings
+import requests
-getVar = get_var.getVar
-
+# Assign initial variables
address = "http://localhost:8080/"
defaultMinLength = 4
defaultMaxLength = 512
+options =
-options = Options()
-options.headless = True
-
-sites = {}
-logos = {}
-configs = {}
-results = {}
-
+# Assign json file variables
with open("../node_modules/cloverleaf/data/sites.json", 'r') as json_file:
sites = json.load(json_file)
-
with open("../data/logos.json", 'r') as json_file:
logos = json.load(json_file)
-
with open("../node_modules/cloverleaf/unit_tests/configs.json", 'r') as json_file:
configs = json.load(json_file)
-
with open("../node_modules/cloverleaf/unit_tests/results.json", 'r') as json_file:
results = json.load(json_file)
sites = deep_merge.merge(sites, logos)
-
-def status_code(driver, url):
- js = '''
- let callback = arguments[0];
- let xhr = new XMLHttpRequest();
- xhr.open('GET', ''' + "'" + url.replace("'", "\\\'") + "'" + ''', true);
- xhr.onload = function () {
- if (this.readyState === 4) {
- callback(this.status);
- }
- };
- xhr.onerror = function () {
- callback('error');
- };
- xhr.send(null);
- '''
-
- return driver.execute_async_script(js)
-
-
-def read_clipboard(driver):
-
- box = driver.find_element_by_id("paste-box")
- box.send_keys(Keys.CONTROL, "v")
- toreturn = box.get_attribute("value")
- box.clear()
- return toreturn
-
+def status_code(url):
+ try:
+ response = requests.head(url)
+ return response.status_code
+ except requests.exceptions.RequestException:
+ return 'error'
@pytest.fixture()
def driver():
- driverInternal = webdriver.Firefox(options=options)
+ driverInternal = webdriver.Firefox(options=Options(headless=True))
try:
driverInternal.get(address)
except WebDriverException:
@@ -78,55 +47,76 @@ def driver():
yield driverInternal
# Close procedures
driverInternal.close()
-
-
-def test_caps_equals_nocaps(driver):
-
- pass_vis.show(driver)
-
+
+# This method ensures no flakey tests.
+@pytest.fixture()
+def clearBoxes():
appElem = driver.find_element_by_id("app")
appElem.clear()
- appElem.send_keys("Test site")
passElem = driver.find_element_by_id("pass")
passElem.clear()
+
+# For the methods pasteBoxSetup and read_clipboard, we make a textbox in order to then paste out the clipboard into it.
+@pytest.fixture()
+def pasteBoxSetup():
+ driver.execute_script(
+ """body = document.querySelector('body');
+ element = document.createElement('textarea');
+ element.id = "paste-box"
+ body.append(element);""")
+
+def read_clipboard(driver):
+ box = driver.find_element_by_id("paste-box")
+ box.send_keys(Keys.CONTROL, "v")
+ toreturn = box.get_attribute("value")
+ box.clear()
+ return toreturn
+
+def find_elements(driver):
+ app_elem = driver.find_element_by_id("app")
+ logo_elem = driver.find_element_by_id("logo")
+ label_elem = driver.find_element_by_xpath("/html/body/div[2]/div/div[1]/label")
+ pass_elem = driver.find_element_by_id("pass")
+
+ return app_elem, logo_elem, label_elem, pass_elem
+
+def test_app_not_case_sensitive(driver):
+ app, logo, label, password = find_elements(driver)
+
+ pass_vis.show(driver)
+
+ # Set name to site with a Caps in it
+ appElem.send_keys("Test site")
passElem.send_keys("Test password")
+ # Get the end resulting password
caps = driver.find_element_by_id("result").get_attribute("value")
appElem.clear()
+
+ # Set name to site without Caps
appElem.send_keys("test site")
-
+
+ # Get the end resulting password
nocaps = driver.find_element_by_id("result").get_attribute("value")
+ # As app is not case sensitive, end resulting password should be the same
assert caps == nocaps, "Output with caps and without is different"
-# Tests to make sure that hitting enter properly applies
+# Tests to make sure that hitting enter properly applies a preset
def test_enter_preset(driver):
+ app, logo, label, password = find_elements(driver)
- appElem = driver.find_element_by_id("app")
- logo = driver.find_element_by_id("logo")
- label = driver.find_element_by_xpath("/html/body/div[2]/div/div[1]/label")
- passElem = driver.find_element_by_id("pass")
-
- # Add box for reading paste
- driver.execute_script(
- """body = document.querySelector('body');
- element = document.createElement('textarea');
- element.id = "paste-box"
- body.append(element);""")
-
+ # For Preset Site Names (Apple, Protonmail...)
for site in sites:
- label.click()
- appElem.clear()
- passElem.clear()
+ label.click() # unsure why click
appElem.send_keys(site)
appElem.send_keys(Keys.ENTER)
- assert appElem.get_attribute("value") == site, "Enter not setting preset name - Preset: " + site
-
+ # TODO: Comment this code, as it is not readable to human readers
try :
if "minLength" in sites[site]:
assert getVar(driver, "minLength") == sites[site]["minLength"], "Enter not setting preset minLength - Preset: " + site
@@ -134,8 +124,7 @@ def test_enter_preset(driver):
if "maxLength" in sites[site]:
assert getVar(driver, "maxLength") == sites[site]["maxLength"], "Enter not setting preset maxLength - Preset: " + site
- # Logo
- logoURL = ""
+ # TODO: Comment this code, as it is not readable to human readers
if "logo" in sites[site]:
logoURL = address + sites[site]["logo"]
else:
@@ -175,14 +164,13 @@ def test_enter_preset(driver):
# Tests to make sure that query strings presets are loaded properly
def test_qs_preset(driver):
-
+ app, logo, label, password = find_elements(driver)
+
+ # For Preset Site Names (Apple, Protonmail...)
for site in sites:
driver.get(address + "?app="+quote(site))
- appElem = driver.find_element_by_id("app")
- logo = driver.find_element_by_id("logo")
-
assert appElem.get_attribute("value") == site, "Query strings not setting preset name - Preset: " + site
if "minLength" in sites[site]: