forked from fediversity/fediversity
		
	Full rework of the Pixelfed test
This commit is contained in:
		
							parent
							
								
									e4ad4e266c
								
							
						
					
					
						commit
						a4cba3f697
					
				
					 3 changed files with 192 additions and 223 deletions
				
			
		|  | @ -8,8 +8,8 @@ | |||
|     { | ||||
|       checks = { | ||||
|         mastodon = import ./tests/mastodon.nix { inherit self pkgs; }; | ||||
|         pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit self pkgs; }; | ||||
|         peertube = import ./tests/peertube.nix { inherit self pkgs; }; | ||||
|         pixelfed = import ./tests/pixelfed.nix { inherit self pkgs; }; | ||||
|       }; | ||||
|     }; | ||||
| } | ||||
|  |  | |||
|  | @ -1,222 +0,0 @@ | |||
| { pkgs, self }: | ||||
| let | ||||
|   lib = pkgs.lib; | ||||
| 
 | ||||
|   ## FIXME: this binding was not used but maybe we want a side effect or something? | ||||
|   # rebuildableTest = import ./rebuildableTest.nix pkgs; | ||||
| 
 | ||||
|   email = "test@test.com"; | ||||
|   password = "testtest"; | ||||
| 
 | ||||
|   # FIXME: Replace all the By.XPATH by By.CSS_SELECTOR. | ||||
| 
 | ||||
|   seleniumImports = '' | ||||
|     import sys | ||||
|     from selenium import webdriver | ||||
|     from selenium.webdriver.common.by import By | ||||
|     from selenium.webdriver.chrome.options import Options | ||||
|   ''; | ||||
| 
 | ||||
|   seleniumSetup = '' | ||||
|     print("Create and configure driver...", file=sys.stderr) | ||||
|     options = Options() | ||||
|     # options.add_argument("--headless=new") | ||||
|     service = webdriver.ChromeService(executable_path="${lib.getExe pkgs.chromedriver}")  # noqa: E501 | ||||
|     driver = webdriver.Chrome(options=options, service=service) | ||||
|     driver.implicitly_wait(30) | ||||
|     driver.set_window_size(1280, 960) | ||||
|   ''; | ||||
| 
 | ||||
|   seleniumPixelfedLogin = '' | ||||
|     print("Open login page...", file=sys.stderr) | ||||
|     driver.get("http://pixelfed.localhost/login") | ||||
|     print("Enter email...", file=sys.stderr) | ||||
|     driver.find_element(By.ID, "email").send_keys("${email}") | ||||
|     print("Enter password...", file=sys.stderr) | ||||
|     driver.find_element(By.ID, "password").send_keys("${password}") | ||||
|     # FIXME: This is disgusting. Find instead the input type submit in the form | ||||
|     # with action ending in "/login". | ||||
|     print("Click “Login” button...", file=sys.stderr) | ||||
|     driver.find_element(By.XPATH, "//button[normalize-space()='Login']").click() | ||||
|   ''; | ||||
| 
 | ||||
|   ## NOTE: `path` must be a valid python string, either a variable or _quoted_. | ||||
|   seleniumTakeScreenshot = path: '' | ||||
|     print("Take screenshot...", file=sys.stderr) | ||||
|     if not driver.save_screenshot(${path}): | ||||
|         raise Exception("selenium could not save screenshot") | ||||
|   ''; | ||||
| 
 | ||||
|   seleniumQuit = '' | ||||
|     print("Quitting...", file=sys.stderr) | ||||
|     driver.quit() | ||||
|   ''; | ||||
| 
 | ||||
|   seleniumScriptPostPicture = | ||||
|     pkgs.writers.writePython3Bin "selenium-script-post-picture" | ||||
|       { libraries = with pkgs.python3Packages; [ selenium ]; } | ||||
|       '' | ||||
|         import os | ||||
|         import time | ||||
|         ${seleniumImports} | ||||
|         from selenium.webdriver.support.wait import WebDriverWait | ||||
| 
 | ||||
|         ${seleniumSetup} | ||||
|         ${seleniumPixelfedLogin} | ||||
|         time.sleep(3) | ||||
| 
 | ||||
|         media_path = os.environ['POST_MEDIA'] | ||||
| 
 | ||||
|         # Find the new post form, fill it in with our pictureand a caption. | ||||
|         print("Click on “Create New Post”...", file=sys.stderr) | ||||
|         driver.find_element(By.LINK_TEXT, "Create New Post").click() | ||||
|         print("Add file to input element...", file=sys.stderr) | ||||
|         driver.find_element(By.XPATH, "//input[@type='file']").send_keys(media_path) | ||||
|         print("Add a caption", file=sys.stderr) | ||||
|         driver.find_element(By.CSS_SELECTOR, ".media-body textarea").send_keys( | ||||
|             "Fediversity test of image upload to pixelfed with garage storage." | ||||
|         ) | ||||
|         time.sleep(3) | ||||
|         print("Click on “Post” button...", file=sys.stderr) | ||||
|         driver.find_element(By.LINK_TEXT, "Post").click() | ||||
| 
 | ||||
|         # Wait until the post loads, and in particular its picture, then take a | ||||
|         # screenshot of the whole page. | ||||
|         print("Wait for post and image to be loaded...", file=sys.stderr) | ||||
|         img = driver.find_element( | ||||
|             By.XPATH, | ||||
|             "//div[@class='timeline-status-component-content']//img" | ||||
|         ) | ||||
|         WebDriverWait(driver, timeout=10).until( | ||||
|             lambda d: d.execute_script("return arguments[0].complete", img) | ||||
|         ) | ||||
|         time.sleep(3) | ||||
| 
 | ||||
|         ${seleniumTakeScreenshot "\"/home/selenium/screenshot.png\""} | ||||
|         ${seleniumQuit}''; | ||||
| 
 | ||||
|   seleniumScriptGetSrc = | ||||
|     pkgs.writers.writePython3Bin "selenium-script-get-src" | ||||
|       { libraries = with pkgs.python3Packages; [ selenium ]; } | ||||
|       '' | ||||
|         ${seleniumImports} | ||||
|         ${seleniumSetup} | ||||
|         ${seleniumPixelfedLogin} | ||||
| 
 | ||||
|         img = driver.find_element( | ||||
|             By.XPATH, | ||||
|             "//div[@class='timeline-status-component-content']//img" | ||||
|         ) | ||||
|         # REVIEW: Need to wait for it to be loaded? | ||||
|         print(img.get_attribute('src')) | ||||
| 
 | ||||
|         ${seleniumQuit}''; | ||||
| 
 | ||||
| in | ||||
| pkgs.nixosTest { | ||||
|   name = "test-pixelfed-garage"; | ||||
| 
 | ||||
|   nodes = { | ||||
|     server = | ||||
|       { config, ... }: | ||||
|       { | ||||
| 
 | ||||
|         services = { | ||||
|           xserver = { | ||||
|             enable = true; | ||||
|             displayManager.lightdm.enable = true; | ||||
|             desktopManager.lxqt.enable = true; | ||||
|           }; | ||||
| 
 | ||||
|           displayManager.autoLogin = { | ||||
|             enable = true; | ||||
|             user = "selenium"; | ||||
|           }; | ||||
|         }; | ||||
|         virtualisation.resolution = { | ||||
|           x = 1680; | ||||
|           y = 1050; | ||||
|         }; | ||||
| 
 | ||||
|         virtualisation = { | ||||
|           memorySize = lib.mkVMOverride 8192; | ||||
|           cores = 8; | ||||
|         }; | ||||
|         imports = with self.nixosModules; [ | ||||
|           fediversity | ||||
|           ../vm/garage-vm.nix | ||||
|           ../vm/pixelfed-vm.nix | ||||
|         ]; | ||||
|         # TODO: pair down | ||||
|         environment.systemPackages = with pkgs; [ | ||||
|           python3 | ||||
|           chromium | ||||
|           chromedriver | ||||
|           xh | ||||
|           seleniumScriptPostPicture | ||||
|           seleniumScriptGetSrc | ||||
|           helix | ||||
|           imagemagick | ||||
|         ]; | ||||
|         environment.variables = { | ||||
|           POST_MEDIA = ./fediversity.png; | ||||
|           AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; | ||||
|           AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; | ||||
|           ## without this we get frivolous errors in the logs | ||||
|           MC_REGION = "garage"; | ||||
|         }; | ||||
|         # chrome does not like being run as root | ||||
|         users.users.selenium = { | ||||
|           isNormalUser = true; | ||||
|         }; | ||||
|       }; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = | ||||
|     { nodes, ... }: | ||||
|     '' | ||||
|       import re | ||||
| 
 | ||||
|       server.start() | ||||
| 
 | ||||
|       with subtest("Pixelfed starts"): | ||||
|         server.wait_for_unit("phpfpm-pixelfed.service") | ||||
| 
 | ||||
|       with subtest("Account creation"): | ||||
|         server.succeed("pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1") | ||||
| 
 | ||||
|       # NOTE: This could in theory give a false positive if pixelfed changes it's | ||||
|       # colorscheme to include pure green. (see same problem in mastodon-garage.nix). | ||||
|       # TODO: For instance: post a red image and check that the green pixel IS NOT | ||||
|       # there, then post a green image and check that the green pixel IS there. | ||||
| 
 | ||||
|       with subtest("Image displays"): | ||||
|         server.succeed("su - selenium -c 'selenium-script-post-picture ${email} ${password}'") | ||||
|         server.copy_from_vm("/home/selenium/screenshot.png", "") | ||||
|         displayed_colors = server.succeed("magick /home/selenium/screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") | ||||
|         # check that the green image displayed somewhere | ||||
|         image_check = re.match(".*#FF0500.*", displayed_colors, re.S) | ||||
|         if image_check is None: | ||||
|           raise Exception("cannot detect the uploaded image on pixelfed page.") | ||||
| 
 | ||||
|       with subtest("access garage"): | ||||
|         server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") | ||||
|         server.succeed("mc ls garage/pixelfed") | ||||
| 
 | ||||
|       with subtest("access image in garage"): | ||||
|         image = server.succeed("mc find garage --regex '\\.png' --ignore '*_thumb.png'") | ||||
|         image = image.rstrip() | ||||
|         if image == "": | ||||
|           raise Exception("image posted to Pixelfed did not get stored in garage") | ||||
|         server.succeed(f"mc cat {image} >/garage-image.png") | ||||
|         garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.png") | ||||
|         image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") | ||||
|         if garage_image_hash != image_hash: | ||||
|           raise Exception("image stored in garage did not match image uploaded") | ||||
| 
 | ||||
|       with subtest("Check that image comes from garage"): | ||||
|         src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") | ||||
|         if not src.startswith("${nodes.server.fediversity.internal.garage.web.urlForBucket "pixelfed"}"): | ||||
|           raise Exception("image does not come from garage") | ||||
|     ''; | ||||
| } | ||||
							
								
								
									
										191
									
								
								services/tests/pixelfed.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								services/tests/pixelfed.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,191 @@ | |||
| { pkgs, self }: | ||||
| 
 | ||||
| let | ||||
|   lib = pkgs.lib; | ||||
| 
 | ||||
|   email = "test@test.com"; | ||||
|   password = "testtest"; | ||||
| 
 | ||||
|   testPicture = pkgs.copyPathToStore ./green.png; | ||||
|   testPictureColour = "#00FF00"; | ||||
| 
 | ||||
|   seleniumScriptPostPicture = | ||||
|     garageBucketUrl: | ||||
|     pkgs.writers.writePython3Bin "selenium-script-post-picture" | ||||
|       { | ||||
|         libraries = with pkgs.python3Packages; [ selenium ]; | ||||
|         flakeIgnore = [ | ||||
|           "E302" | ||||
|           "E305" | ||||
|           "E501" # welcome to the 21st century | ||||
|         ]; | ||||
|       } | ||||
|       '' | ||||
|         import re | ||||
|         import subprocess | ||||
|         import sys | ||||
|         from selenium import webdriver | ||||
|         from selenium.webdriver.common.by import By | ||||
|         from selenium.webdriver.firefox.options import Options | ||||
|         from selenium.webdriver.support.ui import WebDriverWait | ||||
| 
 | ||||
|         print("Create and configure driver...", file=sys.stderr) | ||||
|         options = Options() | ||||
|         options.add_argument("--headless") | ||||
|         service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") | ||||
|         driver = webdriver.Firefox(options=options, service=service) | ||||
|         driver.set_window_size(4096, 2160) | ||||
|         driver.implicitly_wait(360) | ||||
|         wait = WebDriverWait(driver, timeout=720, poll_frequency=60) | ||||
| 
 | ||||
|         ############################################################ | ||||
|         # Login | ||||
| 
 | ||||
|         print("Open login page...", file=sys.stderr) | ||||
|         driver.get("http://pixelfed.localhost/login") | ||||
|         print("Enter email...", file=sys.stderr) | ||||
|         driver.find_element(By.ID, "email").send_keys("${email}") | ||||
|         print("Enter password...", file=sys.stderr) | ||||
|         driver.find_element(By.ID, "password").send_keys("${password}") | ||||
|         print("Click “Login” button...", file=sys.stderr) | ||||
|         driver.find_element(By.XPATH, "//button[normalize-space()='Login']").click() | ||||
| 
 | ||||
|         ############################################################ | ||||
|         # Post picture | ||||
| 
 | ||||
|         # Find the new post form, fill it in with our picture. | ||||
|         print("Click on “Create New Post”...", file=sys.stderr) | ||||
|         driver.find_element(By.LINK_TEXT, "Create New Post").click() | ||||
|         print("Add file to input element...", file=sys.stderr) | ||||
|         driver.find_element(By.XPATH, "//input[@type='file']").send_keys("${testPicture}") | ||||
|         print("Click on “Post” button...", file=sys.stderr) | ||||
|         driver.find_element(By.LINK_TEXT, "Post").click() | ||||
| 
 | ||||
|         # Wait until the post loads, and in particular its picture, then take a | ||||
|         # screenshot of the whole page. | ||||
|         print("Wait for post and picture to be loaded...", file=sys.stderr) | ||||
|         img = driver.find_element(By.XPATH, "//div[@class='timeline-status-component-content']//img") | ||||
|         wait.until(lambda d: d.execute_script("return arguments[0].complete", img)) | ||||
| 
 | ||||
|         ############################################################ | ||||
|         # Check that the picture actually shows | ||||
| 
 | ||||
|         def detect_picture_in_screen(d): | ||||
|             print("  Taking a screenshot...", file=sys.stderr) | ||||
|             d.save_screenshot("/home/selenium/screenshot.png") | ||||
|             print("  Checking it...", file=sys.stderr) | ||||
|             displayed_colours = subprocess.run( | ||||
|                 [ | ||||
|                     "magick", | ||||
|                     "/home/selenium/screenshot.png", | ||||
|                     "-define", | ||||
|                     "histogram:unique-colors=true", | ||||
|                     "-format", | ||||
|                     "%c", | ||||
|                     "histogram:info:", | ||||
|                 ], | ||||
|                 capture_output=True, | ||||
|                 text=True, | ||||
|                 check=True, | ||||
|             ).stdout | ||||
|             result = bool(re.search("${testPictureColour}", displayed_colours, re.IGNORECASE)) | ||||
|             if not result: | ||||
|                 print("  Could not find the picture in the screenshot.", file=sys.stderr) | ||||
|             return result | ||||
| 
 | ||||
|         print("Wait until the picture shows in screen...", file=sys.stderr) | ||||
|         wait.until(detect_picture_in_screen) | ||||
|         print("Picture detected!", file=sys.stderr) | ||||
| 
 | ||||
|         ############################################################ | ||||
|         # Check that the picture gets to Garage | ||||
| 
 | ||||
|         def detect_src_in_garage(d): | ||||
|             print("  Reload the timeline...", file=sys.stderr) | ||||
|             driver.get("http://pixelfed.localhost/") | ||||
|             print("  Getting the picture's src and checking it...", file=sys.stderr) | ||||
|             img = driver.find_element(By.XPATH, "//div[@class='timeline-status-component-content']//img") | ||||
|             result = img.get_attribute('src').startswith("${garageBucketUrl}") | ||||
|             if not result: | ||||
|                 print("  The picture's src does not point to Garage.", file=sys.stderr) | ||||
|             return result | ||||
| 
 | ||||
|         print("Wait until the picture's src points to Garage...", file=sys.stderr) | ||||
|         wait.until(detect_src_in_garage) | ||||
|         print("Picture's src points to Garage!", file=sys.stderr) | ||||
| 
 | ||||
|         ############################################################ | ||||
| 
 | ||||
|         print("Done; bye!", file=sys.stderr) | ||||
|         driver.close() | ||||
|       ''; | ||||
| 
 | ||||
| in | ||||
| 
 | ||||
| pkgs.nixosTest { | ||||
|   name = "pixelfed"; | ||||
| 
 | ||||
|   nodes = { | ||||
|     server = | ||||
|       { config, ... }: | ||||
|       { | ||||
|         imports = with self.nixosModules; [ | ||||
|           fediversity | ||||
|           ../vm/garage-vm.nix | ||||
|           ../vm/pixelfed-vm.nix | ||||
|           ../vm/interactive-vm.nix | ||||
|         ]; | ||||
| 
 | ||||
|         environment.systemPackages = with pkgs; [ | ||||
|           python3 | ||||
|           firefox-unwrapped | ||||
|           geckodriver | ||||
|           xh | ||||
|           (seleniumScriptPostPicture (config.fediversity.internal.garage.web.urlForBucket "pixelfed")) | ||||
|           helix | ||||
|           imagemagick | ||||
|         ]; | ||||
| 
 | ||||
|         environment.variables = { | ||||
|           AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.pixelfed.id; | ||||
|           AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.pixelfed.secret; | ||||
|           ## without this we get frivolous errors in the logs | ||||
|           MC_REGION = "garage"; | ||||
|         }; | ||||
| 
 | ||||
|         # Do not run Selenium scripts as root | ||||
|         users.users.selenium.isNormalUser = true; | ||||
|       }; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = | ||||
|     { nodes, ... }: | ||||
|     '' | ||||
|       server.start() | ||||
| 
 | ||||
|       with subtest("Pixelfed starts"): | ||||
|         server.wait_for_unit("phpfpm-pixelfed.service") | ||||
|         server.succeed("pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1") | ||||
| 
 | ||||
|       # NOTE: This could in theory give a false positive if pixelfed changes it's | ||||
|       # colorscheme to include pure green. (see same problem in mastodon-garage.nix). | ||||
|       # TODO: For instance: post a red picture and check that the green pixel IS NOT | ||||
|       # there, then post a green picture and check that the green pixel IS there. | ||||
| 
 | ||||
|       with subtest("Post an picture in the browser"): | ||||
|         server.succeed("su - selenium -c 'selenium-script-post-picture ${email} ${password}'") | ||||
|         server.copy_from_vm("/home/selenium/screenshot.png", "") | ||||
| 
 | ||||
|       with subtest("Find picture in garage"): | ||||
|         server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") | ||||
|         picture = server.succeed("mc find garage --regex '\\.png' --ignore '*_thumb.png'") | ||||
|         picture = picture.rstrip() | ||||
|         if picture == "": | ||||
|           raise Exception("Could not find any _thumb.png picture stored in Garage") | ||||
|         server.succeed(f"mc cat {picture} > /picture.png") | ||||
|         garage_hash = server.succeed("identify -quiet -format '%#' /picture.png") | ||||
|         hash = server.succeed("identify -quiet -format '%#' ${testPicture}") | ||||
|         if garage_hash != hash: | ||||
|           raise Exception("The picture stored in Garage does not correspond to the original one.") | ||||
|     ''; | ||||
| } | ||||
		Loading…
	
	Add table
		
		Reference in a new issue