1 import ./make-test-python.nix ({ pkgs, lib, ... }:
4 meta = with lib.maintainers; {
5 maintainers = [ alexoundos ];
8 nodes.castopod = { nodes, ... }: {
9 # otherwise 500 MiB file upload fails!
10 virtualisation.diskSize = 512 + 3 * 512;
12 networking.firewall.allowedTCPPorts = [ 80 ];
13 networking.extraHosts =
14 lib.strings.concatStringsSep "\n"
15 (lib.attrsets.mapAttrsToList
16 (name: _: "127.0.0.1 ${name}")
17 nodes.castopod.services.nginx.virtualHosts);
21 database.createLocally = true;
22 localDomain = "castopod.example.com";
23 maxUploadSize = "512M";
27 nodes.client = { nodes, pkgs, lib, ... }:
29 domain = nodes.castopod.services.castopod.localDomain;
32 (builtins.head node.networking.interfaces.eth1.ipv4.addresses).address;
34 targetPodcastSize = 500 * 1024 * 1024;
35 lameMp3Bitrate = 348300;
36 lameMp3FileAdjust = -800;
37 targetPodcastDuration = toString
38 ((targetPodcastSize + lameMp3FileAdjust) / (lameMp3Bitrate / 8));
40 banner = pkgs.runCommand "gen-castopod-cover.jpg" { } ''
41 ${pkgs.imagemagick}/bin/magick `
42 `-background green -bordercolor white -gravity northwest xc:black `
44 `-seed 1 -resize "%[fx:rand()*72+24]" `
45 `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 16x36 `
46 `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "150x50!" `
47 `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append `
48 `-resize ${toString bannerWidth} -quality 1 $out
51 coverWidth = toString 3000;
52 cover = pkgs.runCommand "gen-castopod-banner.jpg" { } ''
53 ${pkgs.imagemagick}/bin/magick `
54 `-background white -bordercolor white -gravity northwest xc:black `
56 `-seed 1 -resize "%[fx:rand()*72+24]" `
57 `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 36x36 `
58 `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "144x144!" `
59 `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append `
60 `-resize ${coverWidth} -quality 1 $out
64 networking.extraHosts =
65 lib.strings.concatStringsSep "\n"
66 (lib.attrsets.mapAttrsToList
67 (name: _: "${getIP nodes.castopod} ${name}")
68 nodes.castopod.services.nginx.virtualHosts);
70 environment.systemPackages =
73 email = "admin@${domain}";
74 password = "Abcd1234";
75 podcastTitle = "Some Title";
76 episodeTitle = "Episode Title";
77 browser-test = pkgs.writers.writePython3Bin "browser-test"
79 libraries = [ pkgs.python3Packages.selenium ];
80 flakeIgnore = [ "E124" "E501" ];
82 from selenium.webdriver.common.by import By
83 from selenium.webdriver import Firefox
84 from selenium.webdriver.firefox.options import Options
85 from selenium.webdriver.firefox.service import Service
86 from selenium.webdriver.support.ui import WebDriverWait
87 from selenium.webdriver.support import expected_conditions as EC
88 from subprocess import STDOUT
91 selenium_logger = logging.getLogger("selenium")
92 selenium_logger.setLevel(logging.DEBUG)
93 selenium_logger.addHandler(logging.StreamHandler())
96 options.add_argument('--headless')
97 service = Service(log_output=STDOUT)
98 driver = Firefox(options=options, service=service)
99 driver = Firefox(options=options)
100 driver.implicitly_wait(30)
101 driver.set_page_load_timeout(60)
103 # install ##########################################################
105 driver.get('http://${domain}/cp-install')
107 wait = WebDriverWait(driver, 20)
109 wait.until(EC.title_contains("installer"))
111 driver.find_element(By.CSS_SELECTOR, '#username').send_keys(
114 driver.find_element(By.CSS_SELECTOR, '#email').send_keys(
117 driver.find_element(By.CSS_SELECTOR, '#password').send_keys(
120 driver.find_element(By.XPATH,
121 "//button[contains(., 'Finish install')]"
124 wait.until(EC.title_contains("Auth"))
126 driver.find_element(By.CSS_SELECTOR, '#email').send_keys(
129 driver.find_element(By.CSS_SELECTOR, '#password').send_keys(
132 driver.find_element(By.XPATH,
133 "//button[contains(., 'Login')]"
136 wait.until(EC.title_contains("Admin dashboard"))
138 # create podcast ###################################################
140 driver.get('http://${domain}/admin/podcasts/new')
142 wait.until(EC.title_contains("Create podcast"))
144 driver.find_element(By.CSS_SELECTOR, '#cover').send_keys(
147 driver.find_element(By.CSS_SELECTOR, '#banner').send_keys(
150 driver.find_element(By.CSS_SELECTOR, '#title').send_keys(
153 driver.find_element(By.CSS_SELECTOR, '#handle').send_keys(
156 driver.find_element(By.CSS_SELECTOR, '#description').send_keys(
159 driver.find_element(By.CSS_SELECTOR, '#owner_name').send_keys(
162 driver.find_element(By.CSS_SELECTOR, '#owner_email').send_keys(
165 driver.find_element(By.XPATH,
166 "//button[contains(., 'Create podcast')]"
169 wait.until(EC.title_contains("${podcastTitle}"))
171 driver.find_element(By.XPATH,
172 "//span[contains(., 'Add an episode')]"
175 wait.until(EC.title_contains("Add an episode"))
177 # upload podcast ###################################################
179 driver.find_element(By.CSS_SELECTOR, '#audio_file').send_keys(
182 driver.find_element(By.CSS_SELECTOR, '#cover').send_keys(
185 driver.find_element(By.CSS_SELECTOR, '#description').send_keys(
186 'Episode description'
188 driver.find_element(By.CSS_SELECTOR, '#title').send_keys(
191 driver.find_element(By.XPATH,
192 "//button[contains(., 'Create episode')]"
195 wait.until(EC.title_contains("${episodeTitle}"))
202 pkgs.firefox-unwrapped
205 (pkgs.writeShellApplication {
207 runtimeInputs = with pkgs; [ sox lame ];
210 sox -n -r 48000 -t wav - synth ${targetPodcastDuration} sine 440 `
211 `| lame --noreplaygain --cbr -q 9 -b 320 - $out
212 FILESIZE="$(stat -c%s $out)"
213 [ "$FILESIZE" -gt 0 ]
214 [ "$FILESIZE" -le "${toString targetPodcastSize}" ]
222 castopod.wait_for_unit("castopod-setup.service")
223 castopod.wait_for_file("/run/phpfpm/castopod.sock")
224 castopod.wait_for_unit("nginx.service")
225 castopod.wait_for_open_port(80)
226 castopod.wait_until_succeeds("curl -sS -f http://castopod.example.com")
228 client.succeed("build-mp3")
230 with subtest("Create superadmin, log in, create and upload a podcast"):
232 "PYTHONUNBUFFERED=1 systemd-cat -t browser-test browser-test")