From 7f1937ffbc32a13c3a6c74acbf0f138d8c38db69 Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Tue, 19 Jul 2022 21:36:56 +0200 Subject: [PATCH] 2.0.2 --- .gitattributes | 1 + CHANGELOG.md | 7 ++ Dockerfile | 7 +- README.md | 19 ++++- backend/src/config.rs | 10 ++- docker-compose.yml | 2 +- package.json | 9 ++- playwright.config.ts | 17 +++-- pnpm-lock.yaml | 6 ++ test/assets/AES.pdf | 3 + test/assets/Pigeons.zip | 3 + .../alfred-kenneally-UIu4RmMxnHU-unsplash.jpg | 3 + test/file/files.ts | 5 ++ test/file/multiple.spec.ts | 11 +++ test/file/simple.spec.ts | 24 +++++++ test/text.ts | 37 ---------- test/text/expiration.spec.ts | 14 ++++ test/text/simple.spec.ts | 8 +++ test/text/views.spec.ts | 18 +++++ test/utils.ts | 71 +++++++++++++++++++ 20 files changed, 217 insertions(+), 58 deletions(-) create mode 100644 test/assets/AES.pdf create mode 100644 test/assets/Pigeons.zip create mode 100644 test/assets/alfred-kenneally-UIu4RmMxnHU-unsplash.jpg create mode 100644 test/file/files.ts create mode 100644 test/file/multiple.spec.ts create mode 100644 test/file/simple.spec.ts delete mode 100644 test/text.ts create mode 100644 test/text/expiration.spec.ts create mode 100644 test/text/simple.spec.ts create mode 100644 test/text/views.spec.ts create mode 100644 test/utils.ts diff --git a/.gitattributes b/.gitattributes index dc5caf9..591bbcf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.afdesign filter=lfs diff=lfs merge=lfs -text +test/assets/** filter=lfs diff=lfs merge=lfs -text diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f156e2..e08eb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.1] - 2022-07-19 + +### Added + +- E2E Tests. +- Make backend more configurable + ## [2.0.1] - 2022-07-18 ### Added diff --git a/Dockerfile b/Dockerfile index 52d503d..2ef3f80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ RUN pnpm run build FROM rust:1.61-alpine as backend WORKDIR /tmp RUN apk add libc-dev openssl-dev alpine-sdk +COPY ./backend/Cargo.* ./ +RUN cargo fetch COPY ./backend ./ RUN cargo build --release @@ -20,7 +22,8 @@ RUN cargo build --release FROM alpine WORKDIR /app COPY --from=backend /tmp/target/release/cryptgeon . -COPY --from=client /tmp/build ./frontend/build -ENV REDIS=redis://redis/ +COPY --from=client /tmp/build ./frontend +ENV FRONTEND_PATH="./frontend" +ENV REDIS="redis://redis/" EXPOSE 5000 ENTRYPOINT [ "/app/cryptgeon" ] diff --git a/README.md b/README.md index 5175cc5..6a25942 100644 --- a/README.md +++ b/README.md @@ -165,8 +165,25 @@ Running `pnpm run dev` in the root folder will start the following things: You can see the app under [localhost:1234](http://localhost:1234). +## Tests + +Tests are end to end tests written with Playwright. + +```sh +pnpm run ci:prepare +docker compose up redis -d +pnpm run ci:server + +# In another terminal. +# Use the test or test:local script. The local version only runs in one browser for quicker development. +pnpm run test:local +``` + ###### Attributions -- Text for tests [Nietzsche Ipsum](https://nietzsche-ipsum.com/) +- Test data: + - Text for tests [Nietzsche Ipsum](https://nietzsche-ipsum.com/) + - [AES Paper](https://www.cs.miami.edu/home/burt/learning/Csc688.012/rijndael/rijndael_doc_V2.pdf) + - [Unsplash Pictures](https://unsplash.com/) - Loading animation by [Nikhil Krishnan](https://codepen.io/nikhil8krishnan/pen/rVoXJa) - Icons made by freepik from www.flaticon.com diff --git a/backend/src/config.rs b/backend/src/config.rs index 32a0cb3..431ae04 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -5,12 +5,10 @@ lazy_static! { pub static ref VERSION: String = option_env!("CARGO_PKG_VERSION") .unwrap_or("Unknown") .to_string(); - pub static ref FRONTEND_PATH: String = option_env!("FRONTEND_PATH") - .unwrap_or("../frontend/build") - .to_string(); - pub static ref LISTEN_ADDR: String = option_env!("LISTEN_ADDR") - .unwrap_or("0.0.0.0:5000") - .to_string(); + pub static ref FRONTEND_PATH: String = + std::env::var("FRONTEND_PATH").unwrap_or("../frontend/build".to_string()); + pub static ref LISTEN_ADDR: String = + std::env::var("LISTEN_ADDR").unwrap_or("0.0.0.0:5000".to_string()); } // CONFIG diff --git a/docker-compose.yml b/docker-compose.yml index 1dfdfb9..5eb2b2b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,4 +16,4 @@ services: environment: SIZE_LIMIT: 128 MiB ports: - - 80:5000 + - 1234:5000 diff --git a/package.json b/package.json index bd95ebf..8d0d460 100644 --- a/package.json +++ b/package.json @@ -5,17 +5,16 @@ "dev:front": "pnpm --prefix frontend run dev", "dev:proxy": "node proxy.mjs", "dev": "run-p dev:*", - "test": "playwright test", - "ci:server": "run-p ci:server:*", - "ci:server:backend": "cd backend && cargo run", - "ci:server:front": "pnpm --prefix frontend run preview", - "ci:server:proxy": "node proxy.mjs", + "test": "playwright test --project chrome firefox safari", + "test:local": "playwright test --project local", + "ci:server": "cd backend && SIZE_LIMIT=10MiB LISTEN_ADDR=0.0.0.0:1234 cargo run", "ci:prepare": "run-p ci:prepare:*", "ci:prepare:backend": "cd backend && cargo build", "ci:prepare:front": "pnpm --prefix frontend install && pnpm --prefix frontend run build" }, "devDependencies": { "@playwright/test": "^1.23.4", + "@types/node": "16", "http-proxy": "^1.18.1", "npm-run-all": "^4.1.5" } diff --git a/playwright.config.ts b/playwright.config.ts index d38c80c..c2e7e78 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -4,21 +4,26 @@ const config: PlaywrightTestConfig = { use: { video: 'retain-on-failure', baseURL: 'http://localhost:1234', - // actionTimeout: 10_000, + actionTimeout: 60_000, }, + outputDir: './test-results', testDir: './test', - testMatch: /.*\.ts/, + webServer: { command: 'pnpm run ci:server', port: 1234, reuseExistingServer: true, - timeout: 20_000, }, projects: [ - { name: 'Chrome', use: { ...devices['Desktop Chrome'] } }, - { name: 'Firefox', use: { ...devices['Desktop Firefox'] } }, - { name: 'Safari', use: { ...devices['Desktop Safari'] } }, + { name: 'chrome', use: { ...devices['Desktop Chrome'] } }, + { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, + { name: 'safari', use: { ...devices['Desktop Safari'] } }, + { + name: 'local', + use: { ...devices['Desktop Chrome'] }, + // testMatch: 'file/too-big.spec.ts', + }, ], } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5d95da..30f9eff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,11 +2,13 @@ lockfileVersion: 5.4 specifiers: '@playwright/test': ^1.23.4 + '@types/node': '16' http-proxy: ^1.18.1 npm-run-all: ^4.1.5 devDependencies: '@playwright/test': 1.23.4 + '@types/node': 16.11.45 http-proxy: 1.18.1 npm-run-all: 4.1.5 @@ -21,6 +23,10 @@ packages: playwright-core: 1.23.4 dev: true + /@types/node/16.11.45: + resolution: {integrity: sha512-3rKg/L5x0rofKuuUt5zlXzOnKyIHXmIu5R8A0TuNDMF2062/AOIDBciFIjToLEJ/9F9DzkHNot+BpNsMI1OLdQ==} + dev: true + /@types/node/18.0.6: resolution: {integrity: sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==} dev: true diff --git a/test/assets/AES.pdf b/test/assets/AES.pdf new file mode 100644 index 0000000..69bcb33 --- /dev/null +++ b/test/assets/AES.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:763d33ef26d06b4e76a0986e276444332df8143c96f72a9ee6e0205becec9c3d +size 1086286 diff --git a/test/assets/Pigeons.zip b/test/assets/Pigeons.zip new file mode 100644 index 0000000..82cbd93 --- /dev/null +++ b/test/assets/Pigeons.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caee5ee6d5de5a80f6dfea466258d58f9a05647594701532403009cac52c6bb0 +size 12401360 diff --git a/test/assets/alfred-kenneally-UIu4RmMxnHU-unsplash.jpg b/test/assets/alfred-kenneally-UIu4RmMxnHU-unsplash.jpg new file mode 100644 index 0000000..1f7a984 --- /dev/null +++ b/test/assets/alfred-kenneally-UIu4RmMxnHU-unsplash.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:750537a30d9c120dd8f573a31fcc38fa5a34ee2f3e2e68eb97e4aa61f5cc1ce8 +size 4581396 diff --git a/test/file/files.ts b/test/file/files.ts new file mode 100644 index 0000000..784e4e0 --- /dev/null +++ b/test/file/files.ts @@ -0,0 +1,5 @@ +export default { + PDF: 'test/assets/AES.pdf', + Image: 'test/assets/alfred-kenneally-UIu4RmMxnHU-unsplash.jpg', + Zip: 'test/assets/Pigeons.zip', +} diff --git a/test/file/multiple.spec.ts b/test/file/multiple.spec.ts new file mode 100644 index 0000000..d006e1e --- /dev/null +++ b/test/file/multiple.spec.ts @@ -0,0 +1,11 @@ +import { test } from '@playwright/test' +import { checkLinkForDownload, createNote, getFileChecksum } from '../utils' +import Files from './files' + +test('multiple', async ({ page }) => { + const files = [Files.PDF, Files.Image] + const checksums = await Promise.all(files.map(getFileChecksum)) + const link = await createNote(page, { files, views: 2 }) + await checkLinkForDownload(page, link, 'alfred-kenneally', checksums[1]) + await checkLinkForDownload(page, link, 'AES.pdf', checksums[0]) +}) diff --git a/test/file/simple.spec.ts b/test/file/simple.spec.ts new file mode 100644 index 0000000..ed538e4 --- /dev/null +++ b/test/file/simple.spec.ts @@ -0,0 +1,24 @@ +import { test } from '@playwright/test' +import { checkLinkDoesNotExist, checkLinkForDownload, checkLinkForText, createNote, getFileChecksum } from '../utils' +import Files from './files' + +test('simple pdf', async ({ page }) => { + const files = [Files.PDF] + const link = await createNote(page, { files }) + await checkLinkForText(page, link, 'AES.pdf') + await checkLinkDoesNotExist(page, link) +}) + +test('pdf content', async ({ page }) => { + const files = [Files.PDF] + const checksum = await getFileChecksum(files[0]) + const link = await createNote(page, { files }) + await checkLinkForDownload(page, link, 'AES.pdf', checksum) +}) + +test('image content', async ({ page }) => { + const files = [Files.Image] + const checksum = await getFileChecksum(files[0]) + const link = await createNote(page, { files }) + await checkLinkForDownload(page, link, 'alfred-kenneally', checksum) +}) diff --git a/test/text.ts b/test/text.ts deleted file mode 100644 index 98dc197..0000000 --- a/test/text.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { expect, test, type Page } from '@playwright/test' - -async function createNote(page: Page, text: string): Promise { - await page.goto('/') - await page.locator('textarea').click() - await page.locator('textarea').fill(text) - await page.locator('button:has-text("create")').click() - - await page.locator('[data-testid="share-link"]').click() - const shareLink = await page.locator('[data-testid="share-link"]').inputValue() - return shareLink -} - -async function checkLinkForText(page: Page, link: string, text: string) { - await page.goto(link) - await page.locator('[data-testid="show-note-button"]').click() - expect(await page.locator('[data-testid="result"] >> .note').innerText()).toBe(text) -} - -async function checkLinkDoesNotExist(page: Page, link: string) { - await page.goto('/') // Required due to firefox: https://github.com/microsoft/playwright/issues/15781 - await page.goto(link) - await expect(page.locator('main')).toContainText('note was not found or was already deleted') -} - -test('simple', async ({ page }) => { - const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of decieve play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.` - const shareLink = await createNote(page, text) - await checkLinkForText(page, shareLink, text) -}) - -test('only shown once', async ({ page }) => { - const text = `Christian victorious reason suicide dead. Right ultimate gains god hope truth burying selfish society joy. Ultimate.` - const shareLink = await createNote(page, text) - await checkLinkForText(page, shareLink, text) - await checkLinkDoesNotExist(page, shareLink) -}) diff --git a/test/text/expiration.spec.ts b/test/text/expiration.spec.ts new file mode 100644 index 0000000..fb9bc21 --- /dev/null +++ b/test/text/expiration.spec.ts @@ -0,0 +1,14 @@ +import { test } from '@playwright/test' +import { checkLinkDoesNotExist, checkLinkForText, createNote } from '../utils' + +test('1 minute', async ({ page }) => { + const text = `Virtues value ascetic revaluation sea dead strong burying.` + const minutes = 1 + const timeout = minutes * 60_000 + test.setTimeout(timeout * 2) + const shareLink = await createNote(page, { text, expiration: minutes }) + await checkLinkForText(page, shareLink, text) + await checkLinkForText(page, shareLink, text) + await page.waitForTimeout(timeout) + await checkLinkDoesNotExist(page, shareLink) +}) diff --git a/test/text/simple.spec.ts b/test/text/simple.spec.ts new file mode 100644 index 0000000..6db0e5f --- /dev/null +++ b/test/text/simple.spec.ts @@ -0,0 +1,8 @@ +import { test } from '@playwright/test' +import { checkLinkForText, createNote } from '../utils' + +test('simple', async ({ page }) => { + const text = `Endless prejudice endless play derive joy eternal-return selfish burying. Of decieve play pinnacle faith disgust. Spirit reason salvation burying strong of joy ascetic selfish against merciful sea truth. Ubermensch moral prejudice derive chaos mountains ubermensch justice philosophy justice ultimate joy ultimate transvaluation. Virtues convictions war ascetic eternal-return spirit. Ubermensch transvaluation noble revaluation sexuality intentions salvation endless decrepit hope noble fearful. Justice ideal ultimate snare god joy evil sexuality insofar gains oneself ideal.` + const shareLink = await createNote(page, { text }) + await checkLinkForText(page, shareLink, text) +}) diff --git a/test/text/views.spec.ts b/test/text/views.spec.ts new file mode 100644 index 0000000..0e9b451 --- /dev/null +++ b/test/text/views.spec.ts @@ -0,0 +1,18 @@ +import { test } from '@playwright/test' +import { checkLinkDoesNotExist, checkLinkForText, createNote } from '../utils' + +test('only shown once', async ({ page }) => { + const text = `Christian victorious reason suicide dead. Right ultimate gains god hope truth burying selfish society joy. Ultimate.` + const shareLink = await createNote(page, { text }) + await checkLinkForText(page, shareLink, text) + await checkLinkDoesNotExist(page, shareLink) +}) + +test('view 3 times', async ({ page }) => { + const text = `Justice holiest overcome fearful strong ultimate holiest christianity.` + const shareLink = await createNote(page, { text, views: 3 }) + await checkLinkForText(page, shareLink, text) + await checkLinkForText(page, shareLink, text) + await checkLinkForText(page, shareLink, text) + await checkLinkDoesNotExist(page, shareLink) +}) diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 0000000..7c841d2 --- /dev/null +++ b/test/utils.ts @@ -0,0 +1,71 @@ +import { expect, type Page } from '@playwright/test' +import { createHash } from 'crypto' +import { readFile } from 'fs/promises' + +type CreatePage = { text?: string; files?: string[]; views?: number; expiration?: number; error?: string } +export async function createNote(page: Page, options: CreatePage): Promise { + await page.goto('/') + + if (options.text) { + await page.locator('[data-testid="text-field"]').fill(options.text) + } else if (options.files) { + await page.locator('[data-testid="switch-file"]').click() + + const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.locator('text=No Files Selected').click(), + ]) + await fileChooser.setFiles(options.files) + } + + if (options.views) { + await page.locator('[data-testid="switch-advanced"]').click() + await page.locator('[data-testid="field-views"]').fill(options.views.toString()) + } else if (options.expiration) { + await page.locator('[data-testid="switch-advanced"]').click() + await page.locator('[data-testid="switch-advanced-toggle"]').click() + await page.locator('[data-testid="field-expiration"]').fill(options.expiration.toString()) + } + + await page.locator('button:has-text("create")').click() + + if (options.error) { + await expect(page.locator('.error-text')).toContainText(options.error, { timeout: 60_000 }) + } + + const shareLink = await page.locator('[data-testid="share-link"]').inputValue() + return shareLink +} + +export async function checkLinkForDownload(page: Page, link: string, text: string, checksum: string) { + await page.goto('/') + await page.goto(link) + await page.locator('[data-testid="show-note-button"]').click() + + const [download] = await Promise.all([ + page.waitForEvent('download'), + page.locator(`[data-testid="result"] >> text=${text}`).click(), + ]) + const path = await download.path() + if (!path) throw new Error('Download failed') + const cs = await getFileChecksum(path) + await expect(cs).toBe(checksum) +} +export async function checkLinkForText(page: Page, link: string, text: string) { + await page.goto('/') + await page.goto(link) + await page.locator('[data-testid="show-note-button"]').click() + await expect(await page.locator('[data-testid="result"] >> .note').innerText()).toContain(text) +} + +export async function checkLinkDoesNotExist(page: Page, link: string) { + await page.goto('/') // Required due to firefox: https://github.com/microsoft/playwright/issues/15781 + await page.goto(link) + await expect(page.locator('main')).toContainText('note was not found or was already deleted') +} + +export async function getFileChecksum(file: string) { + const buffer = await readFile(file) + const hash = createHash('sha3-256').update(buffer).digest('hex') + return hash +}