diff --git a/contrib/playwright/docker-compose.yml b/contrib/playwright/docker-compose.yml index 2670ee449f..8a812d5f1c 100644 --- a/contrib/playwright/docker-compose.yml +++ b/contrib/playwright/docker-compose.yml @@ -6,6 +6,9 @@ networks: name: fmtm-${GIT_BRANCH:-local} services: + proxy: + hostname: fmtm.dev.test + api: hostname: fmtm.dev.test environment: @@ -13,6 +16,8 @@ services: FMTM_DOMAIN: "fmtm.dev.test" # Do not use dev port, use port 80 FMTM_DEV_PORT: "false" + # Override OSM redirect URL to allow login + OSM_LOGIN_REDIRECT_URI: http://fmtm.dev.test/osmauth # The ci image has overrides for CMD and ENTRYPOINT, so re-apply entrypoint: ["/app-entrypoint.sh"] # API_REPLICAS can be increased via CI env vars @@ -36,9 +41,12 @@ services: hostname: fmtm.dev.test environment: VITE_API_URL: "http://fmtm.dev.test:8000" - command: --port 80 + volumes: + - ./contrib/playwright/fmtm.dev.test.crt:/cert/cert.crt + - ./contrib/playwright/fmtm.dev.test.key:/cert/cert.key + command: --port 443 healthcheck: - test: timeout 5s bash -c ':> /dev/tcp/127.0.0.1/80' || exit 1 + test: timeout 5s bash -c ':> /dev/tcp/127.0.0.1/443' || exit 1 interval: 5s retries: 3 start_period: 5s @@ -75,6 +83,7 @@ services: volumes: - ./src/frontend:/app - /tmp/.X11-unix:/tmp/.X11-unix + # - ./contrib/playwright/rootCA.crt:/usr/local/share/ca-certificates/cert.crt:ro entrypoint: /bin/sh -c command: - | diff --git a/contrib/playwright/fmtm.dev.test.crt b/contrib/playwright/fmtm.dev.test.crt new file mode 100644 index 0000000000..228ed07ecb --- /dev/null +++ b/contrib/playwright/fmtm.dev.test.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIUNBnzvf1ACHyhxt8pp0BkzoKHtKowDQYJKoZIhvcNAQEL +BQAwPTEWMBQGA1UEAwwNZm10bS5kZXYudGVzdDELMAkGA1UEBhMCVVMxFjAUBgNV +BAcMDVNhbiBGcmFuc2lzY28wIBcNMjQxMTI1MTkyNjU1WhgPMjEyNDExMDExOTI2 +NTVaMGcxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxv +bmRvbjEPMA0GA1UECgwGSE9UT1NNMQ0wCwYDVQQLDARUZWNoMRYwFAYDVQQDDA1m +bXRtLmRldi50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvVi9 +kQw9i2syYuFAsgLmT6QX6Gk48bCF90/o6dq5nMPzVxcn/aB8bou8A+PG9q8S5Bzb +8nICKZD/h4tjOXoDTbf43ofIzVojp55hAQSKo73EGzd5TZd7tYP9UwAk6xLLVkvq +wcl08t9pDKK2RpWg4hXo/81+audfdU5xEu9NCmlq+TjnvxB3kr8OcNfAsKBbtn2s +J4wblAR/xYewUHxSxzIT/c1ene+Jb4Wk4ifBuOao1l79hPC26nVfG/3MqtlcGzPX +D+F3493DVhGC2ldI6NFThh5nx4ZsJoyilRRO4HYLW+g99fIj2RaZzxShfRgx/5NF +7P+EsxGwi22p/yaJVwIDAQABo3QwcjAfBgNVHSMEGDAWgBSoYr//Gm8c87bdkfbn +qmzEtiqfszAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DAYBgNVHREEETAPgg1mbXRt +LmRldi50ZXN0MB0GA1UdDgQWBBSaWKMj6U+FsUFLnmrlXqaSFU09jTANBgkqhkiG +9w0BAQsFAAOCAQEACNXKFe5M/ACfNYQxjDWHwNgeFltp5KV42mphXAukkQcJG5rS +cXpWwLaaqQrqCVVeFlsei1XXCNT8H4M7BXAZsROThVQZDv8koZ6TebHNX3AC15n+ +CuyjtmFP7dHUx7dSz/o90qMfyYseyh5l+nd/60AS/ZJz0DZ6rsdBBnppvqCvqcrO +55XWf+PfBT+okVqefKGFPm5oS3FHCbLT0NDT26hA+LBAc6xSjklSDZLd5zxauxMu +LUfMni5/olxHFq53WpBuzb0MVT547RiUb21fjHFM9bcHMwv84uwhQ0LHRMJBg6tu +mGJQKL7NSpfaD7P8qLsMqQJ6dlA/2EP2pw8Rwg== +-----END CERTIFICATE----- diff --git a/contrib/playwright/fmtm.dev.test.key b/contrib/playwright/fmtm.dev.test.key new file mode 100644 index 0000000000..e358554e6c --- /dev/null +++ b/contrib/playwright/fmtm.dev.test.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC9WL2RDD2LazJi +4UCyAuZPpBfoaTjxsIX3T+jp2rmcw/NXFyf9oHxui7wD48b2rxLkHNvycgIpkP+H +i2M5egNNt/jeh8jNWiOnnmEBBIqjvcQbN3lNl3u1g/1TACTrEstWS+rByXTy32kM +orZGlaDiFej/zX5q5191TnES700KaWr5OOe/EHeSvw5w18CwoFu2fawnjBuUBH/F +h7BQfFLHMhP9zV6d74lvhaTiJ8G45qjWXv2E8LbqdV8b/cyq2VwbM9cP4Xfj3cNW +EYLaV0jo0VOGHmfHhmwmjKKVFE7gdgtb6D318iPZFpnPFKF9GDH/k0Xs/4SzEbCL +ban/JolXAgMBAAECggEADpSDQGSPZqlx/1A2EZ8MigxqNODJXD8KCdGy7zMDAPHL +zqR/VVXizwZei3QiMYOosj0QqLojtXyvWIogsV/WWkk01jnhegKbZUBaWuA1Jsep +jqj2Y8Wq5n7L5nTH4vwXAUWmRZbUNRGD/s+Wtpe2BELIps31kpjPbnKEXX9k0MzM +FJzjRQ+ETw0Wb2bGNvl2LZzuMiG2fBqDHhgnrhvrxhKKpi+nwocGDd+f79EW4mIk +b3CrCm/IlmlHMZzkdz/fPLEw5ECjpEj9elxg8cFaB2LadmdzVF4P3XhQja3XhuvT +xOXFvZJu0LrVcp/99aYsTq2JsuO0cHf/PVH25rrzcQKBgQD7ptZj0uFjCKUNMb1T +iyxxCTVHdAfunVLUa0+XWVd63EinT7LT7zDwwHfGEBl/TALinoYnBt6AEol3QZrR +hJWyfNGebkmiT4zQ1laZ2ldFHiiRbBMbb4kr+cgDAhbE9lanZL1UfOiQ6TFrItGe +OoRAL+T55VdLqEOY4ZoR6oo25wKBgQDAnk0dLNkNQaq7stq7LdbhNlhObasNi/zC +4omUGEOqsZkpRq3B/hGvY9CtKRqggJIpabulDXAi5bn1WjEpdQcx+RBkcBssz3Vo +UqPWtKdT1Ca/20w371ErMndC+FnHDde6Kq4Wk/2bmnYaliw0w385a/T+N2wx1Hp0 +1cWu7Hd8EQKBgQCajL8UNXn4cWG0dby5pzqW/Tm8dVU9xV8OWA1cfSENsmXoYzkH +kdmzsfCdMlwGYu9OsF7hbwWNEFVeaWski9HH+YJfU4pjZYms5EXxXTfFmhz+Gqqt +adWLrW6Z8ll7g3J6gZMJZWELRjv0eUDVdUithvyQQLpZG7DQHpWZtTpHNwKBgQCU +Ja0d8W6Huaf9cZS2Po107n2A5OyZIMApjF9QnlD9Kleo3YmrGSS1B0pqxKCMLhc8 +7uu6CNnknVgnk1UYk8AS+YNYHLlp/65HrLgC3TONUDD1g0/d/Z0qYkhEJAu5A7aW +o1j+WJjIY0bPMA2UD4D8w0HNfQckVE5TKlA/N/S2YQKBgQC9sQpUr1OgAgSqX11T +AKM2mvgC2Y7GqRzkdNaG/VndodtZXIY4IRV4n0H8UKswdr8Vf5x15JTFhqjVwW2j +ygE+xW/nHXxYwJxkQUZykLTGBJYTfGND1i5cmySu5g/dn95zlMo3AzZSXBIno+LJ +iX23N2haPwNnvmpbmipv5O87vw== +-----END PRIVATE KEY----- diff --git a/contrib/playwright/gen-cert.sh b/contrib/playwright/gen-cert.sh new file mode 100644 index 0000000000..9a1bb2d976 --- /dev/null +++ b/contrib/playwright/gen-cert.sh @@ -0,0 +1,76 @@ +#! /bin/bash + +if [ "$#" -ne 1 ] +then + echo "Error: No domain name argument provided" + echo "Usage: Provide a domain name as an argument" + exit 1 +fi + +DOMAIN=$1 + +# Create root CA & Private key + +openssl req -x509 \ + -sha256 -days 35600 \ + -nodes \ + -newkey rsa:2048 \ + -subj "/CN=${DOMAIN}/C=US/L=San Fransisco" \ + -keyout rootCA.key -out rootCA.crt + +# Generate Private key + +openssl genrsa -out ${DOMAIN}.key 2048 + +# Create csf conf + +cat > csr.conf < cert.conf < { // Specific for this large test, only run in one browser // (playwright.config.ts is configured to run all browsers by default) test.skip(browserName !== 'chromium', 'Test only for chromium!'); - // 0. Temp Login - await tempLogin(page); - await page.getByRole('button', { name: '+ Create New Project' }).click(); - // 1. Project Details Step + await page.goto('/'); + await page.getByRole('button', { name: '+ Create New Project' }).click(); await page.getByRole('button', { name: 'NEXT' }).click(); await expect(page.getByText('Project Name is Required.')).toBeVisible(); await expect(page.getByText('Short Description is Required.', { exact: true })).toBeVisible(); diff --git a/src/frontend/e2e/02-mapper-flow.spec.ts b/src/frontend/e2e/02-mapper-flow.spec.ts index 942ff9d347..87f17e618f 100644 --- a/src/frontend/e2e/02-mapper-flow.spec.ts +++ b/src/frontend/e2e/02-mapper-flow.spec.ts @@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test'; -import { tempLogin, openTestProject } from './helpers'; +import { openTestProject } from './helpers'; test.describe('mapper flow', () => { test('task actions', async ({ browserName, page }) => { @@ -11,11 +11,8 @@ test.describe('mapper flow', () => { // (playwright.config.ts is configured to run all browsers by default) test.skip(browserName !== 'chromium', 'Test only for chromium!'); - // 0. Temp Login - await tempLogin(page); - await openTestProject(page); - // 1. Click on task area on map + await openTestProject(page); await page.locator('canvas').click({ position: { x: 445, diff --git a/src/frontend/e2e/auth.setup.ts b/src/frontend/e2e/auth.setup.ts new file mode 100644 index 0000000000..e8ca5e0aca --- /dev/null +++ b/src/frontend/e2e/auth.setup.ts @@ -0,0 +1,25 @@ +import { test as setup, expect } from '@playwright/test'; +import path from 'path'; + +const authFile = path.join(__dirname, './.auth/user.json'); + +setup('authenticate', async ({ page }) => { + // Navigate to the app's base URL + await page.goto('/'); + await page.getByRole('button', { name: 'Sign in' }).click(); + + // Select OSM login + await page.getByText('Personal OSM Account').click(); + await page.waitForSelector('text=Log in to OpenStreetMap'); + + // OSM Login page + await page.getByLabel('Email Address or Username').fill('FMTM Playwright'); + await page.getByLabel('Password').fill('Boondocks County Send6 Slingshot Villain Vocalist'); + await page.getByRole('button', { name: 'Log in' }).click(); + + // Save authentication state + await page.context().storageState({ path: authFile }); + + // Wait for redirect and valid login (sign out button) + await page.waitForSelector('text=Sign Out'); +}); diff --git a/src/frontend/e2e/helpers.ts b/src/frontend/e2e/helpers.ts index 5c5a3d26da..54b9b92fe9 100644 --- a/src/frontend/e2e/helpers.ts +++ b/src/frontend/e2e/helpers.ts @@ -1,11 +1,5 @@ import { Page } from '@playwright/test'; -export async function tempLogin(page: Page) { - await page.goto('/'); - await page.getByRole('button', { name: 'Sign in' }).click(); - await page.getByText('Temporary Account').click(); -} - export async function openTestProject(page: Page) { // open project card with regex text 'Project Create Playwright xxx' await page diff --git a/src/frontend/playwright.config.ts b/src/frontend/playwright.config.ts index dfa5cf7cbf..2e36538701 100644 --- a/src/frontend/playwright.config.ts +++ b/src/frontend/playwright.config.ts @@ -24,21 +24,40 @@ export default defineConfig({ baseURL: 'http://fmtm.dev.test', // Record a trace for each test, but remove it from successful test runs trace: 'retain-on-failure', + // Simplify access over HTTPS, ignore errors + // Alternatively rootCA.crt can be installed in the Playwright container + // but specific config to make Chrome work is also required + // as it does not use the system CA store + ignoreHTTPSErrors: true, }, /* Configure projects for major browsers */ projects: [ + // Setup project + { name: 'setup', testMatch: /.*\.setup\.ts/ }, { name: 'chromium', - use: { browserName: 'chromium' }, + use: { + browserName: 'chromium', + storageState: 'e2e/.auth/user.json', + }, + dependencies: ['setup'], }, { name: 'firefox', - use: { browserName: 'firefox' }, + use: { + browserName: 'firefox', + storageState: 'e2e/.auth/user.json', + }, + dependencies: ['setup'], }, { name: 'webkit', - use: { browserName: 'webkit' }, + use: { + browserName: 'webkit', + storageState: 'playwright/.auth/user.json', + }, + dependencies: ['setup'], }, /* Test against mobile viewports. */ diff --git a/src/frontend/vite.config.ts b/src/frontend/vite.config.ts index 46a6d8b567..8d0d222fea 100644 --- a/src/frontend/vite.config.ts +++ b/src/frontend/vite.config.ts @@ -50,6 +50,10 @@ export default defineConfig(({ mode }) => { server: { port: 7051, host: '0.0.0.0', + https: { + cert: '/cert/cert.crt', + key: '/cert/cert.key', + }, }, build: { minify: mode === 'development' ? false : 'esbuild',