From 8db5ada567c0182331649da1aa307fcbd31fe733 Mon Sep 17 00:00:00 2001 From: Marshall Cottrell Date: Wed, 22 May 2024 15:39:22 -0400 Subject: [PATCH] chore: add basic smoke test for sonarqube with sso (#77) ## Description Fixes #70 --------- Co-authored-by: zamaz <71521611+zachariahmiller@users.noreply.github.com> Co-authored-by: Wayne Starr --- .github/workflows/test.yaml | 7 +++ .gitignore | 4 ++ tasks.yaml | 4 ++ tasks/test.yaml | 27 +++++++--- tests/auth.setup.ts | 28 ++++++++++ tests/package-lock.json | 104 ++++++++++++++++++++++++++++++++++++ tests/package.json | 9 ++++ tests/playwright.config.ts | 43 +++++++++++++++ tests/sonarqube.test.ts | 19 +++++++ tests/tsconfig.json | 10 ++++ 10 files changed, 248 insertions(+), 7 deletions(-) create mode 100644 tests/auth.setup.ts create mode 100644 tests/package-lock.json create mode 100644 tests/package.json create mode 100644 tests/playwright.config.ts create mode 100644 tests/sonarqube.test.ts create mode 100644 tests/tsconfig.json diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9cc6bd7..acaaa27 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -62,3 +62,10 @@ jobs: uses: defenseunicorns/uds-common/.github/actions/save-logs@b2e8b25930c953ef893e7c787fe350f0d8679ee2 # v0.4.2 with: suffix: ${{ matrix.type }}-${{ matrix.flavor }}-${{ github.run_id }}-${{ github.run_attempt }} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: always() + with: + name: playwright-report-${{ matrix.type }}-${{ matrix.flavor }}-${{ github.run_id }}-${{ github.run_attempt }} + path: tests/.playwright/reports/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 0099811..8151105 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ test/tf/public-ec2-instance/.terraform terraform.tfstate terraform.tfstate.backup .terraform.lock.hcl + +# Tests +node_modules/ +.playwright/ diff --git a/tasks.yaml b/tasks.yaml index 32997f2..3503982 100644 --- a/tasks.yaml +++ b/tasks.yaml @@ -53,8 +53,10 @@ tasks: - task: create-sq-test-bundle - task: setup:k3d-test-cluster - task: deploy:test-bundle + - task: setup:create-doug-user - task: test:health-check - task: test:ingress + - task: test:ui - name: test-upgrade description: Test an upgrade from the latest released package to the current branch @@ -62,7 +64,9 @@ tasks: - task: create-sq-latest-release-bundle - task: setup:k3d-test-cluster - task: deploy:test-bundle + - task: setup:create-doug-user - task: create-sq-test-bundle - task: deploy:test-bundle - task: test:health-check - task: test:ingress + - task: test:ui diff --git a/tasks/test.yaml b/tasks/test.yaml index e2765b2..55fe322 100644 --- a/tasks/test.yaml +++ b/tasks/test.yaml @@ -2,7 +2,7 @@ tasks: - name: health-check actions: # StatefulSets don't show conditions themselves so we look for an underlying Pod - - description: Sonarqube StatefulSet Health Check + - description: SonarQube StatefulSet Health Check wait: cluster: kind: Pod @@ -12,9 +12,22 @@ tasks: - name: ingress actions: - - description: Sonarqube UI Health Check - wait: - network: - protocol: https - address: sonarqube.uds.dev - code: 200 + - description: SonarQube UI Status Check + maxRetries: 30 + cmd: | + STATUS=$(curl -s 'https://sonarqube.uds.dev/api/system/status' | ./uds zarf tools yq '.status') + echo "SonarQube system status: ${STATUS}" + if [ $STATUS != "UP" ]; then + sleep 10 + exit 1 + fi + + - name: ui + description: SonarQube UI Checks + actions: + - cmd: npm ci + dir: tests + - cmd: npx playwright install --with-deps + dir: tests + - cmd: npx playwright test + dir: tests diff --git a/tests/auth.setup.ts b/tests/auth.setup.ts new file mode 100644 index 0000000..60e8538 --- /dev/null +++ b/tests/auth.setup.ts @@ -0,0 +1,28 @@ +import { test as setup, expect } from '@playwright/test'; +import { authFile } from './playwright.config'; + +setup('authenticate', async ({ page, context }) => { + await page.goto('/sessions/new'); + + await page.locator('.identity-provider-link').click(); + await page.getByLabel('Username or email').fill('doug'); + await page.getByLabel('Password').fill('unicorn123!@#'); + + await page.getByRole("button", { name: "Log In" }).click(); + + await page.waitForURL('/projects'); // successful redirect + + // ensure auth cookies were set + const cookies = await context.cookies(); + const keycloakCookie = cookies.find( + (cookie) => cookie.name === "KEYCLOAK_SESSION", + ); + + expect(keycloakCookie).toBeDefined(); + expect(keycloakCookie?.value).not.toBe(""); + expect(keycloakCookie?.domain).toContain("sso."); + + await page.context().storageState({ path: authFile }); + + await expect(page).toHaveURL('/projects'); +}) diff --git a/tests/package-lock.json b/tests/package-lock.json new file mode 100644 index 0000000..a94cd81 --- /dev/null +++ b/tests/package-lock.json @@ -0,0 +1,104 @@ +{ + "name": "uds-package-sonarqube", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "uds-package-sonarqube", + "license": "Apache-2.0", + "devDependencies": { + "@playwright/test": "^1.43.1", + "@types/node": "^20.12.12", + "typescript": "^5.4.5" + } + }, + "node_modules/@playwright/test": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz", + "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==", + "dev": true, + "dependencies": { + "playwright": "1.43.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", + "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", + "dev": true, + "dependencies": { + "playwright-core": "1.43.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", + "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 0000000..6515d20 --- /dev/null +++ b/tests/package.json @@ -0,0 +1,9 @@ +{ + "name": "uds-package-sonarqube", + "license": "Apache-2.0", + "devDependencies": { + "@playwright/test": "^1.43.1", + "@types/node": "^20.12.12", + "typescript": "^5.4.5" + } +} diff --git a/tests/playwright.config.ts b/tests/playwright.config.ts new file mode 100644 index 0000000..0741fbd --- /dev/null +++ b/tests/playwright.config.ts @@ -0,0 +1,43 @@ +import { defineConfig, devices } from '@playwright/test'; + +export const playwrightDir = '.playwright'; +export const authFile = `${playwrightDir}/auth/user.json`; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + fullyParallel: true, + forbidOnly: !!process.env.CI, // fail CI if you accidently leave `test.only` in source + retries: process.env.CI ? 1 : 0, + workers: 1, + reporter: [ + // Reporter to use. See https://playwright.dev/docs/test-reporters + ['html', { outputFolder: `${playwrightDir}/reports`, open: 'never' }], + ['json', { outputFile: `${playwrightDir}/reports/test-results.json`, open: 'never' }], + ['list'] + ], + + outputDir: `${playwrightDir}/output`, + + use: { + baseURL: process.env.BASE_URL || 'https://sonarqube.uds.dev', // for `await page.goto('/')` etc + trace: 'on-first-retry', // collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer + }, + + projects: [ + { name: 'setup', testMatch: /.*\.setup\.ts/ }, // authentication + + ...[ + 'Desktop Chrome', + 'Desktop Firefox', + ].map((p) => ({ + name: devices[p].defaultBrowserType, + dependencies: ['setup'], + use: { + ...devices[p], + storageState: authFile, + }, + })), + ], +}); diff --git a/tests/sonarqube.test.ts b/tests/sonarqube.test.ts new file mode 100644 index 0000000..9a7a5c1 --- /dev/null +++ b/tests/sonarqube.test.ts @@ -0,0 +1,19 @@ +import { test, expect } from "@playwright/test"; + +function randomProjectName() { + return `uds-package-sonarqube-${Math.floor((Math.random() * 1000))}`; +} + +test('create a project', async ({ page }) => { + await page.goto('/projects/create'); + + const projectName = randomProjectName(); + + await page.getByRole('button', { name: 'Manually' }).click(); + await page.getByLabel('Project display name*').fill(projectName); + await page.getByRole('button', { name: 'Set Up' }).click(); + + await expect(page).toHaveURL(`/dashboard?id=${projectName}`); + + await expect(page.getByRole('heading', { level: 1 })).toContainText(projectName); +}); diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..a3b60e8 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "module": "commonjs", /* Specify what module code is generated. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "strict": true, /* Enable all strict type-checking options. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}