global-utils.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import { expect, type Browser, type TestInfo } from '@playwright/test';
  2. import { EventEmitter } from "events";
  3. import { type Mail, MailServer } from 'maildev';
  4. import { execSync } from 'node:child_process';
  5. import dotenv from 'dotenv';
  6. import dotenvExpand from 'dotenv-expand';
  7. const fs = require("fs");
  8. const { spawn } = require('node:child_process');
  9. export function loadEnv(){
  10. var myEnv = dotenv.config({ path: 'test.env' });
  11. dotenvExpand.expand(myEnv);
  12. return {
  13. user1: {
  14. email: process.env.TEST_USER_MAIL,
  15. name: process.env.TEST_USER,
  16. password: process.env.TEST_USER_PASSWORD,
  17. },
  18. user2: {
  19. email: process.env.TEST_USER2_MAIL,
  20. name: process.env.TEST_USER2,
  21. password: process.env.TEST_USER2_PASSWORD,
  22. },
  23. user3: {
  24. email: process.env.TEST_USER3_MAIL,
  25. name: process.env.TEST_USER3,
  26. password: process.env.TEST_USER3_PASSWORD,
  27. },
  28. }
  29. }
  30. export async function waitFor(url: String, browser: Browser) {
  31. var ready = false;
  32. var context;
  33. do {
  34. try {
  35. context = await browser.newContext();
  36. const page = await context.newPage();
  37. await page.waitForTimeout(500);
  38. const result = await page.goto(url);
  39. ready = result.status() === 200;
  40. } catch(e) {
  41. if( !e.message.includes("CONNECTION_REFUSED") ){
  42. throw e;
  43. }
  44. } finally {
  45. await context.close();
  46. }
  47. } while(!ready);
  48. }
  49. export function startComposeService(serviceName: String){
  50. console.log(`Starting ${serviceName}`);
  51. execSync(`docker compose --profile playwright --env-file test.env up -d ${serviceName}`);
  52. }
  53. export function stopComposeService(serviceName: String){
  54. console.log(`Stopping ${serviceName}`);
  55. execSync(`docker compose --profile playwright --env-file test.env stop ${serviceName}`);
  56. }
  57. function wipeSqlite(){
  58. console.log(`Delete Vaultwarden container to wipe sqlite`);
  59. execSync(`docker compose --env-file test.env stop Vaultwarden`);
  60. execSync(`docker compose --env-file test.env rm -f Vaultwarden`);
  61. }
  62. async function wipeMariaDB(){
  63. var mysql = require('mysql2/promise');
  64. var ready = false;
  65. var connection;
  66. do {
  67. try {
  68. connection = await mysql.createConnection({
  69. user: process.env.MARIADB_USER,
  70. host: "127.0.0.1",
  71. database: process.env.MARIADB_DATABASE,
  72. password: process.env.MARIADB_PASSWORD,
  73. port: process.env.MARIADB_PORT,
  74. });
  75. await connection.execute(`DROP DATABASE ${process.env.MARIADB_DATABASE}`);
  76. await connection.execute(`CREATE DATABASE ${process.env.MARIADB_DATABASE}`);
  77. console.log('Successfully wiped mariadb');
  78. ready = true;
  79. } catch (err) {
  80. console.log(`Error when wiping mariadb: ${err}`);
  81. } finally {
  82. if( connection ){
  83. connection.end();
  84. }
  85. }
  86. await new Promise(r => setTimeout(r, 1000));
  87. } while(!ready);
  88. }
  89. async function wipeMysqlDB(){
  90. var mysql = require('mysql2/promise');
  91. var ready = false;
  92. var connection;
  93. do{
  94. try {
  95. connection = await mysql.createConnection({
  96. user: process.env.MYSQL_USER,
  97. host: "127.0.0.1",
  98. database: process.env.MYSQL_DATABASE,
  99. password: process.env.MYSQL_PASSWORD,
  100. port: process.env.MYSQL_PORT,
  101. });
  102. await connection.execute(`DROP DATABASE ${process.env.MYSQL_DATABASE}`);
  103. await connection.execute(`CREATE DATABASE ${process.env.MYSQL_DATABASE}`);
  104. console.log('Successfully wiped mysql');
  105. ready = true;
  106. } catch (err) {
  107. console.log(`Error when wiping mysql: ${err}`);
  108. } finally {
  109. if( connection ){
  110. connection.end();
  111. }
  112. }
  113. await new Promise(r => setTimeout(r, 1000));
  114. } while(!ready);
  115. }
  116. async function wipePostgres(){
  117. const { Client } = require('pg');
  118. const client = new Client({
  119. user: process.env.POSTGRES_USER,
  120. host: "127.0.0.1",
  121. database: "postgres",
  122. password: process.env.POSTGRES_PASSWORD,
  123. port: process.env.POSTGRES_PORT,
  124. });
  125. try {
  126. await client.connect();
  127. await client.query(`DROP DATABASE ${process.env.POSTGRES_DB}`);
  128. await client.query(`CREATE DATABASE ${process.env.POSTGRES_DB}`);
  129. console.log('Successfully wiped postgres');
  130. } catch (err) {
  131. console.log(`Error when wiping postgres: ${err}`);
  132. } finally {
  133. client.end();
  134. }
  135. }
  136. function dbConfig(testInfo: TestInfo){
  137. switch(testInfo.project.name) {
  138. case "postgres":
  139. case "sso-postgres":
  140. return { DATABASE_URL: `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@127.0.0.1:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB}` };
  141. case "mariadb":
  142. case "sso-mariadb":
  143. return { DATABASE_URL: `mysql://${process.env.MARIADB_USER}:${process.env.MARIADB_PASSWORD}@127.0.0.1:${process.env.MARIADB_PORT}/${process.env.MARIADB_DATABASE}` };
  144. case "mysql":
  145. case "sso-mysql":
  146. return { DATABASE_URL: `mysql://${process.env.MYSQL_USER}:${process.env.MYSQL_PASSWORD}@127.0.0.1:${process.env.MYSQL_PORT}/${process.env.MYSQL_DATABASE}`};
  147. case "sqlite":
  148. case "sso-sqlite":
  149. return { I_REALLY_WANT_VOLATILE_STORAGE: true };
  150. default:
  151. throw new Error(`Unknow database name: ${testInfo.project.name}`);
  152. }
  153. }
  154. /**
  155. * All parameters passed in `env` need to be added to the docker-compose.yml
  156. **/
  157. export async function startVault(browser: Browser, testInfo: TestInfo, env = {}, resetDB: Boolean = true) {
  158. if( resetDB ){
  159. switch(testInfo.project.name) {
  160. case "postgres":
  161. case "sso-postgres":
  162. await wipePostgres();
  163. break;
  164. case "mariadb":
  165. case "sso-mariadb":
  166. await wipeMariaDB();
  167. break;
  168. case "mysql":
  169. case "sso-mysql":
  170. await wipeMysqlDB();
  171. break;
  172. case "sqlite":
  173. case "sso-sqlite":
  174. wipeSqlite();
  175. break;
  176. default:
  177. throw new Error(`Unknow database name: ${testInfo.project.name}`);
  178. }
  179. }
  180. console.log(`Starting Vaultwarden`);
  181. execSync(`docker compose --profile playwright --env-file test.env up -d Vaultwarden`, {
  182. env: { ...env, ...dbConfig(testInfo) },
  183. });
  184. await waitFor("/", browser);
  185. console.log(`Vaultwarden running on: ${process.env.DOMAIN}`);
  186. }
  187. export async function stopVault(force: boolean = false) {
  188. if( force === false && process.env.PW_KEEP_SERVICE_RUNNNING === "true" ) {
  189. console.log(`Keep vaultwarden running on: ${process.env.DOMAIN}`);
  190. } else {
  191. console.log(`Vaultwarden stopping`);
  192. execSync(`docker compose --profile playwright --env-file test.env stop Vaultwarden`);
  193. }
  194. }
  195. export async function restartVault(page: Page, testInfo: TestInfo, env, resetDB: Boolean = true) {
  196. stopVault(true);
  197. return startVault(page.context().browser(), testInfo, env, resetDB);
  198. }
  199. export async function checkNotification(page: Page, hasText: string) {
  200. await expect(page.locator('bit-toast').filter({ hasText })).toBeVisible();
  201. await page.locator('bit-toast').filter({ hasText }).getByRole('button').click();
  202. await expect(page.locator('bit-toast').filter({ hasText })).toHaveCount(0);
  203. }
  204. export async function cleanLanding(page: Page) {
  205. await page.goto('/', { waitUntil: 'domcontentloaded' });
  206. await expect(page.getByRole('button').nth(0)).toBeVisible();
  207. const logged = await page.getByRole('button', { name: 'Log out' }).count();
  208. if( logged > 0 ){
  209. await page.getByRole('button', { name: 'Log out' }).click();
  210. await page.getByRole('button', { name: 'Log out' }).click();
  211. }
  212. }
  213. export async function logout(test: Test, page: Page, user: { name: string }) {
  214. await test.step('logout', async () => {
  215. await page.getByRole('button', { name: user.name, exact: true }).click();
  216. await page.getByRole('menuitem', { name: 'Log out' }).click();
  217. await expect(page.getByRole('heading', { name: 'Log in' })).toBeVisible();
  218. });
  219. }