Skip to content

Commit

Permalink
Add playwright create/login test for all db
Browse files Browse the repository at this point in the history
  • Loading branch information
Timshel committed Apr 1, 2024
1 parent 2d98aa3 commit 7d96067
Show file tree
Hide file tree
Showing 10 changed files with 608 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ web-vault

# Vaultwarden Resources
resources

# Playwright tests
playwright
6 changes: 6 additions & 0 deletions playwright/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
logs
node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
temp
63 changes: 63 additions & 0 deletions playwright/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Integration tests

This allows running integration tests using [Playwright](https://playwright.dev/).
\
It usse its own [test.env](/test/scenarios/test.env) with different ports to not collide with a running dev instance.

## Install

```bash
npm install
npx playwright install firefox
```

## Usage

To run all the tests:

```bash
npx playwright test
```

To access the ui to easily run test individually and debug if needed:

```bash
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
npx playwright test --project=sqllite login
```

To run only a specifc test (It might fail if it has dependency):

```bash
npx playwright test --project=sqllite -g "Account creation"
npx playwright test --project=sqllite tests/login.spec.ts:16
```

## 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).
This does not start the server, you will need to start it manually.

```bash
npx playwright codegen "http://127.0.0.1:8000"
```
79 changes: 79 additions & 0 deletions playwright/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { firefox, type FullConfig } from '@playwright/test';
import { exec, execSync } from 'node:child_process';
import fs from 'fs';
import yaml from 'js-yaml';

const utils = require('./global-utils');

utils.loadEnv();

function readCurrentVersion(){
try {
const vw_version_file = fs.readFileSync('temp/web-vault/vw-version.json', {
encoding: 'utf8',
flag: 'r'
});

return JSON.parse(vw_version_file)["version"];
} catch(err) {
console.log(`Failed to read frontend current version: ${err}`);
}
}

function readDockerVersion(){
try {
const docker_settings = fs.readFileSync('../docker/DockerSettings.yaml', {
encoding: 'utf8',
flag: 'r'
});

const settings = yaml.load(docker_settings);
return settings["vault_version"];
} catch(err) {
console.log(`Failed to read docker frontend current version: ${err}`);
}
}

function retrieveFrontend(){
const vw_version = readCurrentVersion();
const vv = readDockerVersion()

if( !vv ){
console.log("Empty docker frontend version");
process.exit(1);
}

try {
if( vv != `v${vw_version}`) {
fs.rmSync("./temp/web-vault", { recursive: true, force: true });

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 {
console.log(`Using existing bw_web_builds-${vv}`);
}
} catch(err) {
console.log(`Failed to retrieve frontend: ${err}`);
process.exit(1);
}
}

function buildServer(){
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 temp/vaultwarden`, { stdio: "inherit" });
} else {
console.log("Using existing server");
}
}

async function globalSetup(config: FullConfig) {
execSync("mkdir -p temp/logs");

buildServer();
retrieveFrontend();
}

export default globalSetup;
139 changes: 139 additions & 0 deletions playwright/global-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { type Browser, type TestInfo } from '@playwright/test';
import { execSync } from 'node:child_process';
import dotenv from 'dotenv';
import dotenvExpand from 'dotenv-expand';

const fs = require("fs");
const { spawn } = require('node:child_process');

function loadEnv(){
var myEnv = dotenv.config({ path: 'test.env' });
dotenvExpand.expand(myEnv);
}

async function waitFor(url: String, browser: Browser) {
var ready = false;
var context;

do {
try {
context = await browser.newContext();
const page = await context.newPage();
await page.waitForTimeout(500);
const result = await page.goto(url);
ready = result.status() === 200;
} catch(e) {
if( !e.message.includes("CONNECTION_REFUSED") ){
throw e;
}
} finally {
await context.close();
}
} while(!ready);
}

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("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}`);

return proc;
}

async function stopVaultwarden(proc, testInfo: TestInfo, resetDB: Boolean = true) {
console.log(`Vaultwarden stopping`);
proc.kill();

if( resetDB ){
switch(testInfo.project.name) {
case "postgres":
stopPostgres();
break;
case "mysql":
stopMariaDB();
break;
default:
startStopSqlite();
}
}
}

async function restartVaultwarden(proc, page: Page, testInfo: TestInfo, env, resetDB: Boolean = true) {
stopVaultwarden(proc, testInfo, resetDB);
return startVaultwarden(page.context().browser(), testInfo, env, resetDB);
}

export { loadEnv, waitFor, startVaultwarden, stopVaultwarden, restartVaultwarden };
Loading

0 comments on commit 7d96067

Please sign in to comment.