From e77b4c46a9315708734b077f9860776a87670db9 Mon Sep 17 00:00:00 2001 From: Timshel Date: Wed, 21 Feb 2024 11:26:24 +0100 Subject: [PATCH] Review fix and improvements --- test/scenarios/.gitignore | 1 + test/scenarios/README.md | 27 +++++ test/scenarios/global-setup.ts | 16 ++- test/scenarios/global-utils.ts | 108 +++++++++++++++++--- test/scenarios/playwright.config.ts | 10 +- test/scenarios/test.env | 19 +--- test/scenarios/tests/login-common.ts | 43 -------- test/scenarios/tests/login.spec.ts | 53 ++++++++-- test/scenarios/tests/login_mariadb.spec.ts | 37 ------- test/scenarios/tests/login_postgres.spec.ts | 37 ------- 10 files changed, 182 insertions(+), 169 deletions(-) delete mode 100644 test/scenarios/tests/login-common.ts delete mode 100644 test/scenarios/tests/login_mariadb.spec.ts delete mode 100644 test/scenarios/tests/login_postgres.spec.ts diff --git a/test/scenarios/.gitignore b/test/scenarios/.gitignore index c50d09fa10f..8746d597aa0 100644 --- a/test/scenarios/.gitignore +++ b/test/scenarios/.gitignore @@ -3,3 +3,4 @@ node_modules/ /test-results/ /playwright-report/ /playwright/.cache/ +temp diff --git a/test/scenarios/README.md b/test/scenarios/README.md index 2c5bd411c4a..9271b763be7 100644 --- a/test/scenarios/README.md +++ b/test/scenarios/README.md @@ -8,6 +8,7 @@ It usse its own [test.env](/test/scenarios/test.env) with different ports to not ```bash npm install +npx playwright install firefox ``` ## Usage @@ -24,6 +25,32 @@ To access the ui to easily run test individually and debug if needed: npx playwright test --ui ``` +### DB + +Projects are configured to allow to run tests only on specific database. +\ +You can use: + +```bash +npx playwright test --project sqllite +npx playwright test --project postgres +npx playwright test --project mysql +``` + +### Running specific tests + +To run a whole file you can : + +```bash +npx playwright test --project sqllite tests/login.spec.ts +``` + +To run only a specifc test (It might fail if it has dependency): + +```bash +npx playwright test --project sqllite -g "Account creation" +``` + ## Writing scenario When creating new scenario use the recorder to more easily identify elements (in general try to rely on visible hint to identify elements and not hidden ids). diff --git a/test/scenarios/global-setup.ts b/test/scenarios/global-setup.ts index 0525040f3a9..fa41567fe13 100644 --- a/test/scenarios/global-setup.ts +++ b/test/scenarios/global-setup.ts @@ -6,11 +6,10 @@ import yaml from 'js-yaml'; const utils = require('./global-utils'); utils.loadEnv(); -var kcPath = process.env.KC_SETUP_PATH; function readCurrentVersion(){ try { - const vw_version_file = fs.readFileSync('data/web-vault/vw-version.json', { + const vw_version_file = fs.readFileSync('temp/web-vault/vw-version.json', { encoding: 'utf8', flag: 'r' }); @@ -46,12 +45,9 @@ function retrieveFrontend(){ try { if( vv != `v${vw_version}`) { - fs.rmSync("./data/web-vault", { recursive: true, force: true }); + fs.rmSync("./temp/web-vault", { recursive: true, force: true }); - execSync(`cd data && wget -c https://github.com/dani-garcia/bw_web_builds/releases/download/${vv}/bw_web_${vv}.tar.gz -O - | tar xz`, { stdio: "inherit" }); - - // Make the SSO button visible - execSync(`bash -c "sed -i 's#a.routerlink=./sso..,##' data/web-vault/app/main.*.css"`, { stdio: "inherit" }); + execSync(`cd temp && wget -c https://github.com/dani-garcia/bw_web_builds/releases/download/${vv}/bw_web_${vv}.tar.gz -O - | tar xz`, { stdio: "inherit" }); console.log(`Retrieved bw_web_builds-${vv}`); } else { @@ -64,17 +60,17 @@ function retrieveFrontend(){ } function buildServer(){ - if( !fs.existsSync('data/vaultwarden') ){ + if( !fs.existsSync('temp/vaultwarden') ){ console.log("Rebuilding server"); execSync(`cd ../.. && cargo build --features sqlite,mysql,postgresql --release`, { stdio: "inherit" }); - execSync(`cp ../../target/release/vaultwarden data/vaultwarden`, { stdio: "inherit" }); + execSync(`cp ../../target/release/vaultwarden temp/vaultwarden`, { stdio: "inherit" }); } else { console.log("Using existing server"); } } async function globalSetup(config: FullConfig) { - execSync("mkdir -p data/logs"); + execSync("mkdir -p temp/logs"); buildServer(); retrieveFrontend(); diff --git a/test/scenarios/global-utils.ts b/test/scenarios/global-utils.ts index 03adf94a158..dc496712583 100644 --- a/test/scenarios/global-utils.ts +++ b/test/scenarios/global-utils.ts @@ -1,4 +1,5 @@ -import { type Browser } from '@playwright/test'; +import { type Browser, type TestInfo } from '@playwright/test'; +import { execSync } from 'node:child_process'; import dotenv from 'dotenv'; import dotenvExpand from 'dotenv-expand'; @@ -31,35 +32,108 @@ async function waitFor(url: String, browser: Browser) { } while(!ready); } -async function startVaultWarden(browser: Browser, env = {}, reset: Boolean = true) { - if( reset ){ - fs.rmSync("data/db.sqlite3", { force: true }); - fs.rmSync("data/db.sqlite3-shm", { force: true }); - fs.rmSync("data/db.sqlite3-wal", { force: true }); +function startStopSqlite(){ + fs.rmSync("temp/db.sqlite3", { force: true }); + fs.rmSync("temp/db.sqlite3-shm", { force: true }); + fs.rmSync("temp/db.sqlite3-wal", { force: true }); +} + +function startMariaDB() { + console.log(`Starting MariaDB`); + execSync(`docker run --rm --name ${process.env.MARIADB_CONTAINER} \ + -e MARIADB_ROOT_PASSWORD=${process.env.MARIADB_PWD} \ + -e MARIADB_USER=${process.env.MARIADB_USER} \ + -e MARIADB_PASSWORD=${process.env.MARIADB_PWD} \ + -e MARIADB_DATABASE=${process.env.MARIADB_DB} \ + -p ${process.env.MARIADB_PORT}:3306 \ + -d mariadb:10.4` + ); +} + + +function stopMariaDB() { + console.log("Stopping MariaDB (ensure DB is wiped)"); + execSync(`docker stop ${process.env.MARIADB_CONTAINER} || true`); +} + +function startPostgres() { + console.log(`Starting Postgres`); + execSync(`docker run --rm --name ${process.env.POSTGRES_CONTAINER} \ + -e POSTGRES_USER=${process.env.POSTGRES_USER} \ + -e POSTGRES_PASSWORD=${process.env.POSTGRES_PWD} \ + -e POSTGRES_DB=${process.env.POSTGRES_DB} \ + -p ${process.env.POSTGRES_PORT}:5432 \ + -d postgres:16.2` + ); +}; + +function stopPostgres() { + console.log("Stopping Postgres (Ensure DB is wiped)"); + execSync(`docker stop ${process.env.POSTGRES_CONTAINER} || true`); +} + +function dbConfig(testInfo: TestInfo){ + switch(testInfo.project.name) { + case "postgres": return { + DATABASE_URL: `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PWD}@127.0.0.1:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB}` + } + case "mysql": return { + DATABASE_URL: `mysql://${process.env.MARIADB_USER}:${process.env.MARIADB_PWD}@127.0.0.1:${process.env.MARIADB_PORT}/${process.env.MARIADB_DB}` + } + default: return { I_REALLY_WANT_VOLATILE_STORAGE: true } + } +} + +async function startVaultwarden(browser: Browser, testInfo: TestInfo, env = {}, resetDB: Boolean = true) { + if( resetDB ){ + switch(testInfo.project.name) { + case "postgres": + stopPostgres(); + startPostgres() + break; + case "mysql": + stopMariaDB(); + startMariaDB(); + break; + default: + startStopSqlite(); + } } - const vw_log = fs.openSync("data/logs/vaultwarden.log", "a"); - var proc = spawn("../../target/release/vaultwarden", { - env: { ...process.env, ...env }, + const vw_log = fs.openSync("temp/logs/vaultwarden.log", "a"); + var proc = spawn("temp/vaultwarden", { + env: { ...process.env, ...env, ...dbConfig(testInfo) }, stdio: [process.stdin, vw_log, vw_log] }); await waitFor("/", browser); - console.log(`VaultWarden running on: ${process.env.DOMAIN}`); + console.log(`Vaultwarden running on: ${process.env.DOMAIN}`); return proc; } -async function stopVaultWarden(proc) { - console.log(`VaultWarden stopping`); +async function stopVaultwarden(proc, testInfo: TestInfo, resetDB: Boolean = true) { + console.log(`Vaultwarden stopping`); proc.kill(); -} -async function restartVaultWarden(proc, browser: Browser, env) { - stopVaultWarden(proc); - return startVaultWarden(browser, env, false); + if( resetDB ){ + switch(testInfo.project.name) { + case "postgres": + stopPostgres(); + break; + case "mysql": + stopMariaDB(); + break; + default: + startStopSqlite(); + } + } } +async function restartVaultwarden(proc, browser: Browser, testInfo: TestInfo, env, resetDB: Boolean = true) { + stopVaultwarden(proc, testInfo, resetDB); + return startVaultwarden(browser, testInfo, env, resetDB); +} -export { loadEnv, waitFor, startVaultWarden, stopVaultWarden, restartVaultWarden }; +export { loadEnv, waitFor, startVaultwarden, stopVaultwarden, restartVaultwarden }; diff --git a/test/scenarios/playwright.config.ts b/test/scenarios/playwright.config.ts index e1e65df8d7b..7f4c9eeb3d9 100644 --- a/test/scenarios/playwright.config.ts +++ b/test/scenarios/playwright.config.ts @@ -39,7 +39,15 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ { - name: 'firefox', + name: 'sqllite', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'postgres', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'mysql', use: { ...devices['Desktop Firefox'] }, }, ], diff --git a/test/scenarios/test.env b/test/scenarios/test.env index 050e743bc3a..950475e5e6e 100644 --- a/test/scenarios/test.env +++ b/test/scenarios/test.env @@ -1,5 +1,5 @@ ################################################################## -### Shared Playwright conf test file VaultWarden and Databases ### +### Shared Playwright conf test file Vaultwarden and Databases ### ################################################################## ############# @@ -7,25 +7,16 @@ ############# TEST_USER=test TEST_USER_PASSWORD=${TEST_USER} -TEST_USER_MAIL="${TEST_USER}@yopmail.com" +TEST_USER_MAIL="${TEST_USER}@example.com" ###################### -# VaultWarden Exec # +# Vaultwarden Config # ###################### +DATA_FOLDER=temp +WEB_VAULT_FOLDER=temp/web-vault/ -###################### -# VaultWarden Config # -###################### ROCKET_PORT=8001 DOMAIN=http://127.0.0.1:${ROCKET_PORT} -I_REALLY_WANT_VOLATILE_STORAGE=true -SSO_ENABLED=true -SSO_ONLY=false -SSO_CLIENT_ID=VaultWarden -SSO_CLIENT_SECRET=VaultWarden -SSO_AUTHORITY=http://${KC_HTTP_HOST}:${KC_HTTP_PORT}/realms/${TEST_REALM} - -WEB_VAULT_FOLDER=data/web-vault/ ########################### # Docker MariaDb container# diff --git a/test/scenarios/tests/login-common.ts b/test/scenarios/tests/login-common.ts deleted file mode 100644 index 7593450d14b..00000000000 --- a/test/scenarios/tests/login-common.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { expect } from '@playwright/test'; - -async function create_account({ page }){ - // Landing page - await page.goto('/'); - await page.getByRole('link', { name: 'Create account' }).click(); - - // Back to Vault create account - await expect(page).toHaveTitle(/Create account/); - await page.getByLabel(/Email address/).fill(process.env.TEST_USER_MAIL); - await page.getByLabel('Name').fill(process.env.TEST_USER); - await page.getByLabel('Master password\n (required)', { exact: true }).fill('Master password'); - await page.getByLabel('Re-type master password').fill('Master password'); - await page.getByRole('button', { name: 'Create account' }).click(); - - // Back to the login page - await expect(page).toHaveTitle('Vaultwarden Web'); - await page.getByLabel('Your new account has been created') - await page.getByRole('button', { name: 'Continue' }).click(); - - // Unlock page - await page.getByLabel('Master password').fill('Master password'); - await page.getByRole('button', { name: 'Log in with master password' }).click(); - - // We are now in the default vault page - await expect(page).toHaveTitle(/Vaults/); -} - -async function login({ page }){ - // Landing page - await page.goto('/'); - await page.getByLabel(/Email address/).fill(process.env.TEST_USER_MAIL); - await page.getByRole('button', { name: 'Continue' }).click(); - - // Unlock page - await page.getByLabel('Master password').fill('Master password'); - await page.getByRole('button', { name: 'Log in with master password' }).click(); - - // We are now in the default vault page - await expect(page).toHaveTitle(/Vaults/); -} - -export { create_account, login }; diff --git a/test/scenarios/tests/login.spec.ts b/test/scenarios/tests/login.spec.ts index 08a8da3d896..ba301024812 100644 --- a/test/scenarios/tests/login.spec.ts +++ b/test/scenarios/tests/login.spec.ts @@ -1,21 +1,54 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, type TestInfo } from '@playwright/test'; const utils = require('../global-utils'); -const { create_account, login } = require('./login-common'); utils.loadEnv(); var proc; -test.beforeAll('Setup', async ({ browser }) => { - proc = await utils.startVaultWarden(browser, { - SSO_ENABLED: false - }); +test.beforeAll('Setup', async ({ browser }, testInfo: TestInfo) => { + proc = await utils.startVaultwarden(browser, testInfo, {}); }); -test.afterAll('Teardown', async () => { - utils.stopVaultWarden(proc); +test.afterAll('Teardown', async ({}, testInfo: TestInfo) => { + utils.stopVaultwarden(proc, testInfo); }); -test('Account creation', create_account); +test('Account creation', async ({ page }) => { + // Landing page + await page.goto('/'); + await page.getByRole('link', { name: 'Create account' }).click(); -test('Master password login', login); + // Back to Vault create account + await expect(page).toHaveTitle(/Create account | Vaultwarden Web/); + await page.getByLabel(/Email address/).fill(process.env.TEST_USER_MAIL); + await page.getByLabel('Name').fill(process.env.TEST_USER); + await page.getByLabel('Master password\n (required)', { exact: true }).fill('Master password'); + await page.getByLabel('Re-type master password').fill('Master password'); + await page.getByRole('button', { name: 'Create account' }).click(); + + // Back to the login page + await expect(page).toHaveTitle('Vaultwarden Web'); + await page.getByLabel('Your new account has been created') + await page.getByRole('button', { name: 'Continue' }).click(); + + // Unlock page + await page.getByLabel('Master password').fill('Master password'); + await page.getByRole('button', { name: 'Log in with master password' }).click(); + + // We are now in the default vault page + await expect(page).toHaveTitle(/Vaults/); +}); + +test('Master password login', async ({ page }) => { + // Landing page + await page.goto('/'); + await page.getByLabel(/Email address/).fill(process.env.TEST_USER_MAIL); + await page.getByRole('button', { name: 'Continue' }).click(); + + // Unlock page + await page.getByLabel('Master password').fill('Master password'); + await page.getByRole('button', { name: 'Log in with master password' }).click(); + + // We are now in the default vault page + await expect(page).toHaveTitle(/Vaults/); +}); diff --git a/test/scenarios/tests/login_mariadb.spec.ts b/test/scenarios/tests/login_mariadb.spec.ts deleted file mode 100644 index 067ef4558a7..00000000000 --- a/test/scenarios/tests/login_mariadb.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { test, expect, type Page } from '@playwright/test'; -import { execSync } from 'node:child_process'; - -const utils = require('../global-utils'); -const { create_account, login } = require('./login-common'); - -utils.loadEnv(); - -var proc; - -test.beforeAll('Setup', async ({ browser }) => { - console.log(`Stopping MariaDB if it's already running`); - execSync(`docker stop ${process.env.MARIADB_CONTAINER} || true`); - console.log(`Starting MariaDB`); - execSync(`docker run --rm --name ${process.env.MARIADB_CONTAINER} \ - -e MARIADB_ROOT_PASSWORD=${process.env.MARIADB_PWD} \ - -e MARIADB_USER=${process.env.MARIADB_USER} \ - -e MARIADB_PASSWORD=${process.env.MARIADB_PWD} \ - -e MARIADB_DATABASE=${process.env.MARIADB_DB} \ - -p ${process.env.MARIADB_PORT}:3306 \ - -d mariadb:10.4` - ); - proc = await utils.startVaultWarden(browser, { - SSO_ENABLED: false, - DATABASE_URL: `mysql://${process.env.MARIADB_USER}:${process.env.MARIADB_PWD}@127.0.0.1:${process.env.MARIADB_PORT}/${process.env.MARIADB_DB}` - }); -}); - -test.afterAll('Teardown', async () => { - utils.stopVaultWarden(proc); - console.log(`Stopping MariaDB`); - execSync(`docker stop ${process.env.MARIADB_CONTAINER}`); -}); - -test('Account creation', create_account); - -test('Master password login', login); diff --git a/test/scenarios/tests/login_postgres.spec.ts b/test/scenarios/tests/login_postgres.spec.ts deleted file mode 100644 index 4a6d4f97b46..00000000000 --- a/test/scenarios/tests/login_postgres.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { test, expect, type Page } from '@playwright/test'; -import { execSync } from 'node:child_process'; - -const utils = require('../global-utils'); -const { create_account, login } = require('./login-common'); - -utils.loadEnv(); - -var proc; - -test.beforeAll('Setup', async ({ browser }) => { - console.log(`Stopping Postgres if it's already running`); - execSync(`docker stop ${process.env.POSTGRES_CONTAINER} || true`); - console.log(`Starting Postgres`); - execSync(`docker run --rm --name ${process.env.POSTGRES_CONTAINER} \ - -e POSTGRES_USER=${process.env.POSTGRES_USER} \ - -e POSTGRES_PASSWORD=${process.env.POSTGRES_PWD} \ - -e POSTGRES_DB=${process.env.POSTGRES_DB} \ - -p ${process.env.POSTGRES_PORT}:5432 \ - -d postgres:16.2` - ); - - proc = await utils.startVaultWarden(browser, { - SSO_ENABLED: false, - DATABASE_URL: `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PWD}@127.0.0.1:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB}` - }); -}); - -test.afterAll('Teardown', async () => { - utils.stopVaultWarden(proc); - console.log(`Stopping Postgres`); - execSync(`docker stop ${process.env.POSTGRES_CONTAINER}`); -}); - -test('Account creation', create_account); - -test('Master password login', login);