From 1b2440e9da96644b7ac061dfe202f2469c16fcb7 Mon Sep 17 00:00:00 2001 From: David Archer <16766645+davidaustinarcher@users.noreply.github.com> Date: Mon, 12 Sep 2022 09:51:54 +0100 Subject: [PATCH] Adding Playwright tests --- Jenkinsfile | 86 ++++++++++++++++++++++++++++++++-- README.md | 2 +- e2e/account.spec.ts | 56 ++++++++++++++++++++++ e2e/attack.spec.ts | 23 ++++++++++ e2e/other.spec.ts | 15 ++++++ e2e/owner.spec.ts | 21 +++++++++ main.tf | 2 +- package.json | 22 +++++++++ playwright.config.ts | 107 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 327 insertions(+), 7 deletions(-) create mode 100644 e2e/account.spec.ts create mode 100644 e2e/attack.spec.ts create mode 100644 e2e/other.spec.ts create mode 100644 e2e/owner.spec.ts create mode 100644 package.json create mode 100644 playwright.config.ts diff --git a/Jenkinsfile b/Jenkinsfile index e1e282a..814bce5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,8 @@ pipeline { } sh """ terraform init - npm i puppeteer + npm install @playwright/test + npm init playwright@latest -- --quiet --browser=chromium """ } } @@ -44,8 +45,11 @@ pipeline { export ARM_CLIENT_SECRET=$AZURE_CLIENT_SECRET export ARM_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID export ARM_TENANT_ID=$AZURE_TENANT_ID - - terraform apply -auto-approve -var 'location=$location' -var 'initials=$initials' -var 'environment=qa' -var 'servername=jenkins' -var 'session_metadata="branchName=${env.GIT_BRANCH},buildNumber=${BUILD_NUMBER},commitHash=${env.GIT_SHORT_COMMIT},version=1.5.1"' + terraform apply -auto-approve -var 'location=$location' \ + -var 'initials=$initials' \ + -var 'environment=qa' \ + -var 'servername=jenkins' \ + -var 'session_metadata=branchName=qa,committer=Ryan,buildNumber=${env.BUILD_NUMBER}' """ } catch (Exception e) { echo "Terraform refresh failed, deleting state" @@ -67,16 +71,88 @@ pipeline { } } } - stage('exercise') { + stage('exercise - qa') { steps { timeout(20) { sh """ FQDN=\$(terraform output fqdn) - BASEURL=\$FQDN node exercise.js + BASEURL=\$FQDN npx playwright test """ } } } + stage('provision - dev') { + steps { + script { + withCredentials([azureServicePrincipal('ContrastAzureSponsored')]) { + try { + sh """ + export ARM_CLIENT_ID=$AZURE_CLIENT_ID + export ARM_CLIENT_SECRET=$AZURE_CLIENT_SECRET + export ARM_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID + export ARM_TENANT_ID=$AZURE_TENANT_ID + terraform apply -auto-approve -var 'location=$location' \ + -var 'initials=$initials' \ + -var 'environment=development' \ + -var 'servername=Macbook-Pro' \ + -var 'session_metadata=branchName=feat: improve owner search,committer=Jake,buildNumber=${env.BUILD_NUMBER}' + """ + } catch (Exception e) { + echo "Terraform refresh failed, deleting state" + sh "rm -rf terraform.tfstate" + currentBuild.result = "FAILURE" + error("Aborting the build.") + } + } + } + } + } + stage('exercise - dev') { + steps { + catchError(buildResult: 'SUCCESS', stageResult: 'ABORTED') { + timeout(20) { + sh """ + FQDN=\$(terraform output fqdn) + BASEURL=\$FQDN npx playwright test + """ + } + } + } + } + stage('provision - prod') { + steps { + script { + withCredentials([azureServicePrincipal('ContrastAzureSponsored')]) { + try { + sh """ + export ARM_CLIENT_ID=$AZURE_CLIENT_ID + export ARM_CLIENT_SECRET=$AZURE_CLIENT_SECRET + export ARM_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID + export ARM_TENANT_ID=$AZURE_TENANT_ID + terraform apply -auto-approve -var 'location=$location' -var 'initials=$initials' -var 'environment=production' -var 'servername=Prod-01' + """ + } catch (Exception e) { + echo "Terraform refresh failed, deleting state" + sh "rm -rf terraform.tfstate" + currentBuild.result = "FAILURE" + error("Aborting the build.") + } + } + } + } + } + stage('exercise - prod') { + steps { + catchError(buildResult: 'SUCCESS', stageResult: 'ABORTED') { + timeout(20) { + sh """ + FQDN=\$(terraform output fqdn) + BASEURL=\$FQDN npx playwright test e2e/attack.spec.ts + """ + } + } + } + } stage('destroy') { steps { withCredentials([azureServicePrincipal('ContrastAzureSponsored')]) { diff --git a/README.md b/README.md index 7588481..9a22865 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ You can run PetClinic locally on any machine with Java 1.8 RE installed. 1. Place a `contrast.jar` into the application's root folder. 1. Run the application using: ```sh -java -javaagent:contrast.jar -Dcontrast.config.path=contrast_security.yaml -Dcontrast.agent.java.standalone_app_name=spring-petclinic -jar spring-petclinic-1.5.1.jar [--server.port=8080] [--server.address=localhost] +java -javaagent:contrast.jar -Dcontrast.config.path=contrast_security.yaml -Dcontrast.application.name=spring-petclinic -jar spring-petclinic-1.5.1.jar [--server.port=8080] [--server.address=localhost] ``` 1. Browse the application at http://localhost:8080/ diff --git a/e2e/account.spec.ts b/e2e/account.spec.ts new file mode 100644 index 0000000..5d534c1 --- /dev/null +++ b/e2e/account.spec.ts @@ -0,0 +1,56 @@ +import { test, expect } from '@playwright/test'; + +test.describe('owners', () => { + test('find owner', async ({ page }) => { + await page.goto('/owners/find'); + await page.locator('input[name="lastName"]').fill('davis'); + + await page.locator('button:has-text("Find Owner")').click(); + await expect(page.locator("text=Betty Davis")).toHaveCount(1) + }) + + test('view owner', async ({ page }) => { + await page.goto('/owners/2'); + await expect(page.locator("text=Betty Davis")).toHaveCount(1) + }) + + test('edit owner', async ({ page }) => { + await page.goto('/owners/2'); + await page.locator('a:has-text("Edit Owner")').click(); + await page.locator('button:has-text("Update Owner")').click(); + await expect(page.locator("text=Betty Davis")).toHaveCount(1) + }) + + test('edit pet', async ({ page }) => { + await page.goto('/owners/2/pets/2/edit'); + await page.locator('button:has-text("Update Pet")').click(); + await expect(page.locator("text=Betty Davis")).toHaveCount(1) + }) + + test('add pet', async ({ page }) => { + await page.goto('/owners/2/pets/new'); + await page.locator('input[name="name"]').fill('Rover'); + await page.locator('input[name="birthDate"]').fill('2001-01-01'); + await page.locator('button:has-text("Add Pet")').click(); + await expect(page.locator("text=Rover")).toHaveCount(1) + }) + + test('add owner', async ({ page }) => { + await page.goto('/owners/new'); + await page.locator('input[name="lastName"]').fill('Doe'); + await page.locator('input[name="firstName"]').fill('Jane'); + await page.locator('input[name="address"]').fill('1 Main Street'); + await page.locator('input[name="city"]').fill('Chicago'); + await page.locator('input[name="telephone"]').fill('555947343'); + await page.locator('button:has-text("Add Owner")').click(); + await expect(page.locator("text=Jane Doe")).toHaveCount(1) + }) + + test('add visit', async ({ page }) => { + await page.goto('/owners/2/pets/2/visits/new'); + await page.locator('input[name="description"]').fill('Vaccination'); + await page.locator('button:has-text("Add Visit")').click(); + await expect(page.locator("text=Vaccination")).toHaveCount(1) + }) + + }); \ No newline at end of file diff --git a/e2e/attack.spec.ts b/e2e/attack.spec.ts new file mode 100644 index 0000000..94ecdb6 --- /dev/null +++ b/e2e/attack.spec.ts @@ -0,0 +1,23 @@ +import { test, expect } from '@playwright/test'; + +test.describe('attacks', () => { + test('find owner', async ({ page }) => { + await page.goto('/owners/find'); + await page.locator('input[name="lastName"]').fill('\' OR \'1\'=\'1'); + + await page.locator('button:has-text("Find Owner")').click(); + await expect(page.locator("text=George Franklin")).toHaveCount(1) + }) + + test('edit owner', async ({ page }) => { + page.on('dialog', async (dialog) => { + expect(dialog.message()).toEqual('1') + await dialog.accept() + }) + + await page.goto('/owners/2'); + await page.locator('a:has-text("Edit Owner")').click(); + await page.locator('input[name="address"]').fill(''); + await page.locator('button:has-text("Update Owner")').click(); + }) + }); \ No newline at end of file diff --git a/e2e/other.spec.ts b/e2e/other.spec.ts new file mode 100644 index 0000000..49fca2b --- /dev/null +++ b/e2e/other.spec.ts @@ -0,0 +1,15 @@ +import { test, expect } from '@playwright/test'; + +test.describe('other', () => { + test('error page', async ({ page }) => { + await page.goto('/oups'); + + await expect(page.locator("text=Something happened...")).toHaveCount(1) + }) + + test('visit home page', async ({ page }) => { + await page.goto('/'); + + await expect(page.locator("text=Welcome")).toHaveCount(1) + }) + }); \ No newline at end of file diff --git a/e2e/owner.spec.ts b/e2e/owner.spec.ts new file mode 100644 index 0000000..9b3017d --- /dev/null +++ b/e2e/owner.spec.ts @@ -0,0 +1,21 @@ +import { test, expect } from '@playwright/test'; + +test.describe('vets', () => { + test('view as html', async ({ page }) => { + await page.goto('/vets.html'); + + await expect(page.locator("text=James Carter")).toHaveCount(1) + }) + + test('view as xml', async ({ page }) => { + await page.goto('/vets.xml'); + await expect(page).toHaveURL(/.*vets.xml/); + }) + + test('view as json', async ({ page }) => { + await page.goto('/vets.json'); + await expect(page).toHaveURL(/.*vets.json/); + }) + + + }); \ No newline at end of file diff --git a/main.tf b/main.tf index 57b5ee2..42323e6 100644 --- a/main.tf +++ b/main.tf @@ -33,7 +33,7 @@ resource "azurerm_container_group" "app" { protocol = "TCP" } environment_variables = { - JAVA_TOOL_OPTIONS = "-javaagent:/opt/contrast/contrast.jar -Dcontrast.agent.java.standalone_app_name=spring-petclinic -Dcontrast.api.url=${data.external.yaml.result.url} -Dcontrast.api.api_key=${data.external.yaml.result.api_key} -Dcontrast.api.service_key=${data.external.yaml.result.service_key} -Dcontrast.api.user_name=${data.external.yaml.result.user_name} -Dcontrast.standalone.appname=${var.appname} -Dcontrast.server.name=${var.servername} -Dcontrast.server.environment=${var.environment} -Dcontrast.application.session_metadata=${var.session_metadata} -Dcontrast.application.tags=${var.apptags} -Dcontrast.server.tags=${var.servertags}" + JAVA_TOOL_OPTIONS = "-javaagent:/opt/contrast/contrast.jar -Dcontrast.application.name=spring-petclinic -Dcontrast.api.url=${data.external.yaml.result.url} -Dcontrast.api.api_key=${data.external.yaml.result.api_key} -Dcontrast.api.service_key=${data.external.yaml.result.service_key} -Dcontrast.api.user_name=${data.external.yaml.result.user_name} -Dcontrast.standalone.appname=${var.appname} -Dcontrast.server.name=${var.servername} -Dcontrast.server.environment=${var.environment} -Dcontrast.application.session_metadata=${var.session_metadata} -Dcontrast.application.tags=${var.apptags} -Dcontrast.server.tags=${var.servertags}" } } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..7188708 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "demo-petclinic", + "version": "1.0.0", + "description": "This sample application is based on https://github.com/Contrast-Security-OSS/spring-petclinic", + "scripts": { + "e2e": "npx playwright test" + }, + "repository": { + "type": "git", + "url": "git+https://davidaustinarcher@github.com/Contrast-Security-OSS/demo-petclinic.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Contrast-Security-OSS/demo-petclinic/issues" + }, + "homepage": "https://github.com/Contrast-Security-OSS/demo-petclinic#readme", + "devDependencies": { + "@playwright/test": "^1.25.2" + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..3a4eefe --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,107 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './e2e', + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000 + }, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.BASEURL || 'http://localhost:8080', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + + // { + // name: 'firefox', + // use: { + // ...devices['Desktop Firefox'], + // }, + // }, + + // { + // name: 'webkit', + // use: { + // ...devices['Desktop Safari'], + // }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; + +export default config;