mirror of
https://github.com/paritytech/banana_split.git
synced 2026-09-12 19:51:03 +05:00
add end-to-end UI tests. Resolves #30
* add 2e2 tests * fix prettier * add workflow * prettify * update vue/cli-service * update readme
This commit is contained in:
parent
49ee8905e6
commit
cbdfc7e08b
@ -3,7 +3,6 @@ module.exports = {
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:vue/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:security/recommended"
|
||||
],
|
||||
parserOptions: {
|
||||
|
||||
25
.github/workflows/e2e-test.yaml
vendored
Normal file
25
.github/workflows/e2e-test.yaml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: End to end test
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
e2e-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Read .nvmrc
|
||||
run: echo NVMRC=`cat .nvmrc` >> $GITHUB_ENV
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NVMRC }}
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
|
||||
- name: Install operating system dependencies
|
||||
run: npx playwright install-deps
|
||||
|
||||
- run: npm run test:e2e
|
||||
9
.github/workflows/yarn.yml
vendored
9
.github/workflows/yarn.yml
vendored
@ -7,7 +7,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2-beta
|
||||
|
||||
- name: Read .nvmrc
|
||||
run: echo NVMRC=`cat .nvmrc` >> $GITHUB_ENV
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NVMRC }}
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -20,4 +20,4 @@ yarn-error.log*
|
||||
*.sln
|
||||
*.sw*
|
||||
|
||||
.test-results
|
||||
test-results
|
||||
|
||||
@ -65,11 +65,16 @@ yarn run serve
|
||||
yarn run build
|
||||
```
|
||||
|
||||
#### Run your tests
|
||||
#### Run unit tests
|
||||
```
|
||||
yarn run test:unit
|
||||
```
|
||||
|
||||
#### Run end-to-end UI tests
|
||||
```
|
||||
yarn run test:e2e
|
||||
```
|
||||
|
||||
#### Lints and fixes files
|
||||
```
|
||||
yarn run lint
|
||||
|
||||
17
package.json
17
package.json
@ -12,7 +12,8 @@
|
||||
"build": "vue-cli-service build",
|
||||
"deploy": "gh-pages -b gh-pages -d dist -a",
|
||||
"lint": "vue-cli-service lint",
|
||||
"test:unit": "vue-cli-service test:unit"
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.0",
|
||||
@ -27,12 +28,14 @@
|
||||
"vue-router": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.16.3",
|
||||
"@types/fluent-ffmpeg": "^2.1.19",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@typescript-eslint/parser": "^4.31.2",
|
||||
"@vue/cli-plugin-eslint": "^4.5.13",
|
||||
"@vue/cli-plugin-typescript": "^4.5.13",
|
||||
"@vue/cli-plugin-unit-jest": "^4.5.13",
|
||||
"@vue/cli-service": "^3.1.4",
|
||||
"@vue/cli-plugin-eslint": "^4.5.15",
|
||||
"@vue/cli-plugin-typescript": "^4.5.15",
|
||||
"@vue/cli-plugin-unit-jest": "^4.5.15",
|
||||
"@vue/cli-service": "^4.5.15",
|
||||
"@vue/test-utils": "^1.0.0-beta.20",
|
||||
"eslint": "^5.8.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
@ -40,10 +43,12 @@
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"fast-text-encoding": "^1.0.0",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"gh-pages": "^2.0.1",
|
||||
"html-webpack-inline-source-plugin": "1.0.0-beta.2",
|
||||
"html-webpack-plugin": "4.0.0-beta.4",
|
||||
"jest-junit": "^6.0.1",
|
||||
"playwright": "^1.16.3",
|
||||
"ts-jest": "^26.4.4",
|
||||
"vue-template-compiler": "^2.5.17",
|
||||
"vuetype": "^0.3.2"
|
||||
@ -58,7 +63,7 @@
|
||||
"last 2 versions"
|
||||
],
|
||||
"jest-junit": {
|
||||
"outputDirectory": ".test-results/jest",
|
||||
"outputDirectory": "test-results/jest",
|
||||
"oputputName": "results.xml"
|
||||
}
|
||||
}
|
||||
|
||||
13
playwright.config.ts
Normal file
13
playwright.config.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { PlaywrightTestConfig } from "@playwright/test";
|
||||
const config: PlaywrightTestConfig = {
|
||||
webServer: {
|
||||
command: "NODE_ENV=test npm run serve -- --port 8888",
|
||||
port: 8888,
|
||||
reuseExistingServer: false
|
||||
},
|
||||
testDir: "tests/e2e",
|
||||
use: {
|
||||
browserName: "chromium"
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
@ -15,10 +15,10 @@
|
||||
<GeneralInfo />
|
||||
<div class="measure">
|
||||
<div v-if="secure">
|
||||
<router-link class="button-nav" to="/share">
|
||||
<router-link id="shareNav" class="button-nav" to="/share">
|
||||
Create
|
||||
</router-link>
|
||||
<router-link class="button-nav" to="/combine">
|
||||
<router-link id="combineNav" class="button-nav" to="/combine">
|
||||
Restore
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
<p>
|
||||
<label>1. Secret Phrase</label>
|
||||
<input
|
||||
id="passphrase"
|
||||
v-model="passphrase"
|
||||
type="text"
|
||||
placeholder="type your passphrase"
|
||||
@ -23,11 +24,16 @@
|
||||
</p>
|
||||
<p>
|
||||
<label>2. Secret</label>
|
||||
<textarea v-if="recoveredSecret" v-model="recoveredSecret" readonly />
|
||||
<textarea
|
||||
v-if="recoveredSecret"
|
||||
id="recoveredSecret"
|
||||
v-model="recoveredSecret"
|
||||
readonly
|
||||
/>
|
||||
<textarea v-else readonly disabled />
|
||||
</p>
|
||||
<div v-if="!recoveredSecret">
|
||||
<button class="button-card" @click="reconstruct">
|
||||
<button id="reconstructBtn" class="button-card" @click="reconstruct">
|
||||
Reconstruct Secret
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
<p>
|
||||
<label>1. Name of your split</label>
|
||||
<input
|
||||
id="secretTitle"
|
||||
v-model="title"
|
||||
type="text"
|
||||
:disabled="encryptionMode"
|
||||
@ -17,6 +18,7 @@
|
||||
<p>
|
||||
<label>2. Secret</label>
|
||||
<textarea
|
||||
id="secret"
|
||||
v-model="secret"
|
||||
:class="{ tooLong: secretTooLong }"
|
||||
:disabled="encryptionMode"
|
||||
@ -31,6 +33,7 @@
|
||||
<br />
|
||||
Will require any {{ requiredShards }} shards out of
|
||||
<input
|
||||
id="totalShards"
|
||||
v-model.number="totalShards"
|
||||
:disabled="encryptionMode"
|
||||
type="number"
|
||||
@ -40,12 +43,22 @@
|
||||
to reconstruct
|
||||
</p>
|
||||
<button
|
||||
id="generateBtn"
|
||||
class="button-card"
|
||||
:disabled="secretTooLong"
|
||||
:hidden="encryptionMode"
|
||||
v-on:click="toggleMode"
|
||||
>
|
||||
<span v-if="encryptionMode">Back to editing data</span>
|
||||
<span v-else>Generate QR codes!</span>
|
||||
Generate QR codes!
|
||||
</button>
|
||||
<button
|
||||
id="backToEditBtn"
|
||||
class="button-card"
|
||||
:disabled="secretTooLong"
|
||||
:hidden="!encryptionMode"
|
||||
v-on:click="toggleMode"
|
||||
>
|
||||
Back to editing data
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -60,7 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" transparent="true">
|
||||
<button class="button-card" @click="print">
|
||||
<button id="printBtn" class="button-card" @click="print">
|
||||
Print us!
|
||||
</button>
|
||||
<shard-info
|
||||
@ -99,7 +112,7 @@ export default Vue.extend({
|
||||
title: "",
|
||||
secret: "",
|
||||
totalShards: 3, // TODO: 5
|
||||
recoveryPassphrase: passPhrase.generate(4),
|
||||
recoveryPassphrase: "",
|
||||
encryptionMode: false
|
||||
};
|
||||
},
|
||||
@ -130,12 +143,19 @@ export default Vue.extend({
|
||||
return [];
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.regenPassphrase();
|
||||
},
|
||||
mounted: function() {
|
||||
this.$eventHub.$emit("foldGeneralInfo");
|
||||
this.$eventHub.$emit("clearAlerts");
|
||||
},
|
||||
methods: {
|
||||
regenPassphrase: function() {
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
this.recoveryPassphrase = "TEST";
|
||||
return;
|
||||
}
|
||||
this.recoveryPassphrase = passPhrase.generate(4);
|
||||
},
|
||||
print: function() {
|
||||
|
||||
51
tests/e2e/index.spec.ts
Normal file
51
tests/e2e/index.spec.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { chromium } from "playwright";
|
||||
import { makeVideoFromImages, scaleImages } from "./util/ffmpeg";
|
||||
|
||||
test("happy path", async ({ page, baseURL }, testInfo) => {
|
||||
const secret = "top secret";
|
||||
const images = `%01d.png`;
|
||||
const video = "fake-camera.y4m";
|
||||
|
||||
await test.step("create QR codes", async () => {
|
||||
await page.goto(`${baseURL}`);
|
||||
await page.click("#shareNav");
|
||||
await page.fill("#secretTitle", "Title");
|
||||
await page.fill("#secret", secret);
|
||||
await page.click("#generateBtn");
|
||||
|
||||
const qrCodes = page.locator("#print .qr-tile .card-qr canvas");
|
||||
await expect(qrCodes).toHaveCount(3);
|
||||
await qrCodes.first().screenshot({ path: testInfo.outputPath("1.png") });
|
||||
await qrCodes.last().screenshot({ path: testInfo.outputPath("2.png") });
|
||||
});
|
||||
|
||||
await test.step("make qr code images the same size", async () => {
|
||||
await scaleImages(testInfo.outputPath(images));
|
||||
});
|
||||
|
||||
await test.step("make video from qr codes", async () => {
|
||||
await makeVideoFromImages(
|
||||
testInfo.outputPath(images),
|
||||
testInfo.outputPath(video)
|
||||
);
|
||||
});
|
||||
|
||||
await test.step("restore secret", async () => {
|
||||
const browser = await chromium.launch({
|
||||
args: [
|
||||
"--use-fake-ui-for-media-stream",
|
||||
"--use-fake-device-for-media-stream",
|
||||
`--use-file-for-fake-video-capture=${testInfo.outputPath(video)}`,
|
||||
],
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.goto(`${baseURL}`);
|
||||
await page.click("#combineNav");
|
||||
await page.waitForSelector("#passphrase");
|
||||
await page.fill("#passphrase", "TEST");
|
||||
await page.click("#reconstructBtn");
|
||||
await expect(page.locator("#recoveredSecret")).toHaveValue(secret);
|
||||
});
|
||||
});
|
||||
27
tests/e2e/util/ffmpeg.ts
Normal file
27
tests/e2e/util/ffmpeg.ts
Normal file
@ -0,0 +1,27 @@
|
||||
const ffmpeg = require("fluent-ffmpeg");
|
||||
|
||||
export function scaleImages(imagesPattern: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ffmpeg()
|
||||
.input(imagesPattern)
|
||||
.outputOptions(["-vf scale=200:200"])
|
||||
.output(imagesPattern)
|
||||
.on("end", resolve)
|
||||
.on("error", reject)
|
||||
.run();
|
||||
});
|
||||
}
|
||||
|
||||
export function makeVideoFromImages(imagesPattern: string, videoPath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ffmpeg()
|
||||
.input(imagesPattern)
|
||||
.inputFPS(24)
|
||||
.videoFilters("pad=360:240", "format=yuv420p")
|
||||
.fps(24)
|
||||
.output(videoPath)
|
||||
.on("end", resolve)
|
||||
.on("error", reject)
|
||||
.run();
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user